React useCallback 引用问题和闭包。在渲染时重新创建 UseCallback

React useCallback reference issue and closures . usecallback recreating on renders

提问人:workK 提问时间:11/13/2023 最后编辑:ChinezworkK 更新时间:11/15/2023 访问量:31

问:

import React, { useCallback, useState } from 'react';

const App = () => {
  const [count, setCount] = useState(0);

  const increase = useCallback(() => {
    let count = null;
    function incrementByOne() {
      count = count + 1;
      console.log(count);
    }
    return incrementByOne;
  }, []);

  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <button onClick={increase()}>Increase</button>
    </>
  );
};

export default App;

从上面的代码中,当我单击“增加”按钮(秒)时,计数增加 1 并对其进行控制台化,它不会呈现,但是当我单击第一个按钮时,状态值会发生变化并且组件会呈现。然后当我单击第二个按钮时,计数从 1 开始,即使在使用 useCallback 后,重新渲染后,前一个计数也在初始化为零,重新渲染时的 increase 方法重新创建,谁能解释为什么该值在渲染后没有持久化,并且 useCallback 再次重新创建?

JavaScript ReactJS 闭包

评论

0赞 Bergi 11/13/2023
当您的组件由于状态更改而重新呈现时,您将再次调用,在新的 .increase()incrementByOnecount

答:

0赞 Bergi 11/13/2023 #1

问题在于没有返回其他函数。当您的组件由于状态更改而重新呈现时,您将再次调用,在新的 .您可以通过添加 ,在上面看到这一点。useCallbackincrease()incrementByOnecountconsole.trace('re-initialising count')increaselet count = null;

要解决此问题,您需要确保在安装组件时仅创建一次闭包,而不是在每次渲染时创建闭包。可以使用 ,但更可靠的解决方案是使用 的初始值设定器功能,只是从不更新状态:useMemo()useState

function App() {
  const [count, setCount] = useState(0);

  const [increment, _] = useState(() => {
    let count = 0;
    return () => {
      count = count + 1;
      console.log(count);
    };
  });

  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <button onClick={increment}>Increase</button>
    </>
  );
}

然而,惯用的 React 方法1 将完全避免闭包和可变变量,而是使用 ref:

function App() {
  const [count, setCount] = useState(0);

  const countRef = useRef(0);
  const increment = () => {
    countRef.current = countRef.current + 1;
    console.log(countRef.current);
  };

  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <button onClick={increment}>Increase</button>
    </>
  );
}

1:或者说,你不应该在没有重新渲染的情况下改变任何东西,所以你可以只使用正常状态......