React Hooks useCallback 的简单示例的问题
Posted
技术标签:
【中文标题】React Hooks useCallback 的简单示例的问题【英文标题】:Trouble with simple example of React Hooks useCallback 【发布时间】:2019-07-22 18:44:05 【问题描述】:我正在尝试制作一个简单的示例,该示例遵循页面 https://reactjs.org/docs/hooks-reference.html#usecallback 文档中的 React Hooks 示例
没有useCallback
,代码在这个例子中可以找到:
import React, useCallback from "react";
function Test(props)
function doSomething(a, b)
console.log("doSomething called");
return a + b;
return (
<div>
Array.from( length: 3 ).map(() => (
<div>doSomething('aaa','bbb')</div>
))
</div>
);
export default Test;
但是,当我如下添加我认为正确的 useCallback
代码时,我收到一个错误(a 未定义)
import React, useCallback from "react";
function Test(props)
function doSomething(a, b)
console.log("doSomething called");
return a + b;
const memoizedCallback = useCallback(
() =>
doSomething(a, b);
,
[a, b]
);
return (
<div>
Array.from( length: 3 ).map(() => (
<div>memoizedCallback("aaa", "bbb")</div>
))
</div>
);
export default Test;
问题代码在这里:
https://stackblitz.com/edit/react-usememo2?file=Hello.js
【问题讨论】:
【参考方案1】:useCallback
的目的是能够利用当前范围内的道具或状态,并且在重新渲染时可能会发生变化。当你需要新版本的回调时,依赖数组会告诉 React。如果你想记住一个昂贵的计算,你需要使用useMemo
。
下面的示例演示了useCallback
和useMemo
之间的区别以及不使用它们的后果。在这个例子中,我使用React.memo
来防止Child
重新渲染,除非它的道具或状态发生变化。这样可以看到useCallback
的好处。现在如果Child
收到一个新的onClick
道具,它将导致重新渲染。
子 1 正在接收一个非记忆的 onClick
回调,因此每当父组件重新渲染时,子 1 总是会收到一个新的 onClick
函数,因此它会被强制重新渲染。
孩子 2 正在使用从 useCallback
返回的记忆化的 onClick
回调,而孩子 3 正在使用通过 useMemo
的等价物来演示
useCallback(fn, inputs) 等价于 useMemo(() => fn, inputs)
对于子 2 和 3,每次单击子 2 或 3 时仍会执行回调,useCallback
只是确保在依赖项未更改时传递相同版本的 onClick
函数。
显示的以下部分有助于指出正在发生的事情:
nonMemoizedCallback === memoizedCallback: false|true
另外,我正在显示somethingExpensiveBasedOnA
和使用useMemo
的记忆版本。出于演示目的,我使用了一个不正确的依赖数组(我故意省略了b
),以便您可以看到当b
更改时记忆化版本不会更改,但当a
更改时它确实会更改。每当a
或b
更改时,非记忆版本就会更改。
import ReactDOM from "react-dom";
import React,
useRef,
useMemo,
useEffect,
useState,
useCallback
from "react";
const Child = React.memo(( onClick, suffix ) =>
const numRendersRef = useRef(1);
useEffect(() =>
numRendersRef.current++;
);
return (
<div onClick=() => onClick(suffix)>
Click Me to log a and suffix and change b. Number of Renders:" "
numRendersRef.current
</div>
);
);
function App(props)
const [a, setA] = useState("aaa");
const [b, setB] = useState("bbb");
const computeSomethingExpensiveBasedOnA = () =>
console.log("recomputing expensive thing", a);
return a + b;
;
const somethingExpensiveBasedOnA = computeSomethingExpensiveBasedOnA();
const memoizedSomethingExpensiveBasedOnA = useMemo(
() => computeSomethingExpensiveBasedOnA(),
[a]
);
const nonMemoizedCallback = suffix =>
console.log(a + suffix);
setB(prev => prev + "b");
;
const memoizedCallback = useCallback(nonMemoizedCallback, [a]);
const memoizedCallbackUsingMemo = useMemo(() => nonMemoizedCallback, [a]);
return (
<div>
A: a
<br />
B: b
<br />
nonMemoizedCallback === memoizedCallback:" "
String(nonMemoizedCallback === memoizedCallback)
<br />
somethingExpensiveBasedOnA: somethingExpensiveBasedOnA
<br />
memoizedSomethingExpensiveBasedOnA: memoizedSomethingExpensiveBasedOnA
<br />
<br />
<div onClick=() => setA(a + "a")>Click Me to change a</div>
<br />
<Child onClick=nonMemoizedCallback suffix="1" />
<Child onClick=memoizedCallback suffix="2" />
<Child onClick=memoizedCallbackUsingMemo suffix="3" />
</div>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
这是一个相关的答案:React Hooks useCallback causes child to re-render
【讨论】:
在第一个示例中,它仍然输出了 3 次 console.log。我没有得到什么? (我猜是个难题) 你只是误解了useCallback
的目的。 useCallback
不会记住函数的结果——它会记住函数本身。在第一个示例中,没有充分的理由使用它,但在第二个示例中,它将阻止 Child
的 onClick
属性在重新渲染时发生变化。如果你想记住结果(以防止重复执行昂贵的函数),你应该使用useMemo
。
你说的太对了,我不明白。我被文档中的这条评论所困扰:useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs).
我正在把课件放在一起,有点迷失,寻找好的例子。
我已将我的示例替换为不再基于您的代码的示例,但如果您花足够的时间研究它、与之交互并查看结果,这应该有助于解决问题。
以上是关于React Hooks useCallback 的简单示例的问题的主要内容,如果未能解决你的问题,请参考以下文章
react hooks 中的 useEffect, useCallback, useMemo
react hooks 中的 useEffect, useCallback, useMemo
react hooks 中的 useEffect, useCallback, useMemo
ZF_react hooks useState的实现 useCallback useMemo useReducer useContext