useCallback
和 useMemo
是 React hooks 的功能,旨在绕过函数式编程方法的弱点。在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 -事件处理程序为相同,这要归功于 useCallback 函数调用。它将始终返回函数的相同实例,并使 React 组件渲染机制保持顺畅。
通过 useCallback 内部存储函数将导致新问题。存储的函数调用实例将无法直接访问当前函数调用的变量。相反,它将看到在创建存储函数的初始闭包调用中引入的变量。因此,对于更新的变量,该调用将不起作用。这就是为什么需要告诉 useCallback 某些使用的变量是否已更改。因此,useCallback 将把当前函数调用实例存储为新的存储实例。第二个参数作为 useCallback 的变量列表列出了此功能的变量。在我们的示例中,我们需要告诉 useCallback 函数,我们需要在每次调用时使用一个新的 counter 变量版本。如果不这样做,调用后计数器值将始终为 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
调用也是出于同样的原因。函数块没有内部状态,所以钩子使用 useState
、useCallback
和 useMemo
来模拟类的基本功能。从这个意义上讲,函数式编程是向过程式编程更接近的一个大步骤回归到历史中去了。
useMemo
是与 useCallback
相同类型的机制,但用于其他对象和变量。通过它,您可以限制组件重新呈现的需求,因为如果列出的字段未更改,则 useMemo
函数将在每次函数调用时返回相同的值。
这种新的 React 钩子方法中的一部分无疑是系统最薄弱的地方。useCallback
相当令人费解,而且容易出错。使用 useCallback
调用和依赖项时,很容易陷入内部循环。这一点在 React Class 方法中是不存在的。
毕竟,使用类的原始方法更有效率。 useCallback
会减少重新呈现的需求,但它会在某些相关变量发生更改时再次生成函数,并匹配变量是否已更改本身会产生开销。这可能导致不必要的多次重新呈现。而在 React 类中就不存在这种情况。
useCallback
,因为返回值是记忆化的。 - Yangshun TayuseCallback
的第二个参数设置为[]
,以便告诉React无论参数如何都要进行记忆化处理。 - Supasate