javascript 为什么'useCallback'不能总是返回相同的ref

nszi6y05  于 2023-01-29  发布在  Java
关注(0)|答案(2)|浏览(158)

我不明白为什么useCallback每次更新deps时都会返回一个新的ref,这会导致React.memo()本可以避免的很多重渲染。
useCallback的这个实现有什么问题(如果有的话)?

export function useCallback(callback) {

    const callbackRef = useRef();

    callbackRef.current = callback;

    return useState(() =>
        (...args) => callbackRef.current(...args)
    )[0];

}

使用这个方法而不是内置的实现肯定会对性能产生显著的积极影响。

自己的结论:

    • 没有理由不使用一个使用ref的实现来代替内建的**in,只要你意识到它的含义,也就是说,正如@Bergy所指出的,你不能存储一个回调以供以后使用(例如在setTimeout之后),并期望回调具有与你同步调用它相同的效果。

不过在我看来,这是首选的行为,所以没有缺点。🥂.

更新:

有一个React RFC引入了一个内置的钩子,它可以被称为useEvent

hwamh0ep

hwamh0ep1#

useCallback的这个实现有什么问题(如果有的话)?
我怀疑当有人存储对回调的引用以备将来使用时会产生意想不到的后果,因为它会改变回调正在做的事情:

const { Fragment, useCallback, useState } = React;

function App() {
  const [value, setValue] = useState("");
  const printer = useCallback(() => value, [value]);
  return <div>
    <input type="text" value={value} onChange={e => setValue(e.currentTarget.value)} />
    <Example printer={printer} />
  </div>
}

function Example({printer}) {
  const [printerHistory, setHistory] = useState([]);
  return <Fragment>
    <ul>{
      printerHistory.map(printer => <li>{printer()}</li>)
    }</ul>
    <button onClick={e => setHistory([...printerHistory, printer])}>Store</button>
  </Fragment>
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://unpkg.com/react@16.14.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.14.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

(Sure,在这个简化的演示中,printer回调只不过是value本身上的一个无用闭包,但是您可以想象一个更复杂的情况,其中您可以选择单个历史记录条目,并希望在回调中使用复杂的按需计算)
对于原生useCallback,存储在printerHistory中的函数将是不同值上的不同闭包,而对于您的实现,它们都将是同一个函数,引用最新的useCallback参数,并且在每次调用时只打印当前值。
更详细的描述请参见useEvent提案,其中肯定有一些用例,比如solvingstaleclosure问题,但这与useCallback解决的问题不同。

2exbekwf

2exbekwf2#

useCallback和useMemo的用例是不同的,您说过useCallback返回回调的记忆版本,只有当其中一个依赖项发生变化时才会发生变化,因此它会根据依赖项的变化重新呈现组件,但useMemo只保存可变值,通常我们使用useCallback来处理事件处理程序,例如,假设当您单击按钮时,显示按钮的点击次数,这里使用useCallback实现click事件。

const [count, setCount] = useState(0);
 const handleClick = useCallback(() => {
   setCount(count + 1);
   console.log(count + 1);
 }, [count]);

要获得新的增加的计数值并在每次单击按钮时显示它的值,需要重新呈现。再说一遍,useCallback和useMemo的用例取决于用途。

相关问题