0

在进行代码审查时,我遇到了这个自定义钩子:

import { useRef, useEffect, useCallback } from 'react'

export default function useLastVersion (func) {
  const ref = useRef()
  useEffect(() => {
    ref.current = func
  }, [func])
  return useCallback((...args) => {
    return ref.current(...args)
  }, [])
}

这个钩子是这样使用的:

const f = useLastVersion(() => { // do stuff and depends on props })

基本上,与const f = useCallBack(() => { // do stuff }, [dep1, dep2])此相比,避免声明依赖项列表并且f永远不会更改,即使其中一个依赖项发生更改。

我不知道如何看待这段代码。我不明白使用useLastVersion相比有什么缺点useCallback

4

2 回答 2

1

这个问题实际上已经或多或少地在文档中得到了回答:https ://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-sharing-value-from-usecallback

有趣的部分是:

另请注意,此模式可能会导致并发模式出现问题。我们计划在未来提供更符合人体工程学的替代方案,但目前最安全的解决方案是,如果某个值取决于更改,则始终使回调无效。

也很有趣的阅读:https ://github.com/facebook/react/issues/14099和https://github.com/reactjs/rfcs/issues/83

当前的建议是使用提供者来避免在 props 中传递回调,如果我们担心会导致过多的重新渲染。

于 2021-01-21T13:32:27.670 回答
0

如评论中所述,我的观点是,当依赖项更改过于频繁时(在useEffect/ useCallbackdep 数组中),这个钩子在“你得到多少渲染”方面是多余的,使用普通函数是最好的选择(没有高架)。

这个钩子隐藏了使用它的组件的渲染,但渲染来自useEffect其父级。

如果我们总结我们得到的渲染计数:

  • Ref + useCallback(钩子):Render in Component(due to value) + Render in hook ( useEffect),一共2个。
  • useCallback only:Render in Component(due to value) + render in Counter(change in function reference duo to valuechange),一共2个。
  • 正常功能:渲染输入Component+渲染输入Counter:每次渲染新功能,总共2个。

但是在useEffector中进行浅层比较会得到额外的开销useCallback

实际例子:

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

function useLastVersion(func) {
  const ref = useRef();
  useEffect(() => {
    ref.current = func;
    console.log("useEffect called in ref+callback");
  }, [func]);
  return useCallback((...args) => {
    return ref.current(...args);
  }, []);
}

function Component({ value }) {
  const f1 = useLastVersion(() => {
    alert(value.length);
  });

  const f2 = useCallback(() => {
    alert(value.length);
  }, [value]);

  const f3 = () => {
    alert(value.length);
  };

  return (
    <div>
      Ref and useCallback:{" "}
      <MemoCounter callBack={f1} msg="ref and useCallback" />
      Callback only: <MemoCounter callBack={f2} msg="callback only" />
      Normal: <MemoCounter callBack={f3} msg="normal" />
    </div>
  );
}

function Counter({ callBack, msg }) {
  console.log(msg);
  return <button onClick={callBack}>Click Me</button>;
}

const MemoCounter = React.memo(Counter);

编辑使用最后一个版本


作为旁注,如果目的只是找到input最小渲染的长度,那么阅读inputRef.current.value将是解决方案。

于 2021-01-21T15:13:48.543 回答