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

下面的示例演示了useCallbackuseMemo 之间的区别以及不使用它们的后果。在这个例子中,我使用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 更改时它确实会更改。每当ab 更改时,非记忆版本就会更改。

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 不会记住函数的结果——它会记住函数本身。在第一个示例中,没有充分的理由使用它,但在第二个示例中,它将阻止 ChildonClick 属性在重新渲染时发生变化。如果你想记住结果(以防止重复执行昂贵的函数),你应该使用useMemo 你说的太对了,我不明白。我被文档中的这条评论所困扰:useCallback(fn, inputs) is equivalent to useMemo(() =&gt; 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

React Hooks(钩子函数)

useEffect, useCallback, useMemo三者有何区别?