useCallback
并useMemo
试图绕过使用 React 钩子选择的函数式编程方法所带来的弱点。在 Javascript 中,每个实体,无论是函数、变量还是其他任何实体,都会在执行进入函数的代码块时创建到内存中。对于将尝试检测组件是否需要渲染的 React 来说,这是一个大问题。根据输入道具和上下文扣除重新渲染的需求。让我们看一个没有useCallback
.
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = () => {
setCounter(counter + 1);
}
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
请注意,将在块内的每个函数调用上创建 handleClick 函数实例,因此每次调用的事件处理程序的地址将不同。因此,React 框架将始终将事件处理程序视为已更改。在上面的示例中,React 将在每次调用时将 handleClick 视为一个新值。它根本没有工具可以将其区分为同一个调用。
做什么useCallback
,如果列出的变量没有改变,它会在内部存储函数的第一个引入版本并将其返回给调用者。
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = useCallback(() => {
setCounter(counter + 1);
}, [])
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
现在,使用上面的代码,React 将识别handleClick
-event 处理程序,这要归功于 -functionuseCallback
调用。它将始终返回相同的函数实例,并且 React 组件渲染机制会很高兴。
将函数存储在内部useCallback
会带来一个新问题。函数调用的存储实例将无法直接访问当前函数调用的变量。相反,它将看到在创建存储函数的初始闭包调用中引入的变量。因此,该调用不适用于更新的变量。这就是为什么你需要知道一些使用的变量是否已经改变。这样 useCallback 会将当前的函数调用实例存储为一个新的存储实例。作为第二个参数的变量useCallback
列表列出了此功能的变量。在我们的示例中,我们需要告诉useCallback
-函数,我们需要在每次调用时都有一个新版本的 counter -variable。如果我们不这样做,调用后的计数器值将始终为 1,它来自原始值 0 加 1。
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = useCallback(() => {
setCounter(counter + 1);
}, [counter])
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
现在我们有了一个不会在每次调用时重新渲染的代码的工作版本。
很高兴注意到useState
-call 出于同样的原因在这里。功能块没有内部状态,所以钩子使用useState
,useCallback
和useMemo
模仿类的基本功能。从这个意义上说,函数式编程是历史上更接近过程式编程的一大步。
useMemo
与其他对象和变量的机制相同useCallback
。使用它,您可以限制对组件重新渲染的需求,因为useMemo
如果列出的字段没有更改,-function 将在每个函数调用上返回相同的值。
这部分新的 React hooks 方法绝对是系统的最薄弱环节。useCallback
非常违反直觉并且非常容易出错。使用useCallback
-calls 和依赖关系,很容易最终追逐内部循环。我们在 React Class 方法中没有这个警告。
毕竟,最初的类方法更有效。这useCallback
将减少重新渲染的需要,但每次它的某些因变量发生变化时,它都会重新生成函数,如果变量本身发生变化,匹配就会产生开销。这可能会导致不必要的重新渲染。React 类不是这种情况。