【问题标题】:Best practice for indicating prop is used in useEffect or similar在 useEffect 或类似中使用指示 prop 的最佳实践
【发布时间】:2020-11-28 13:21:34
【问题描述】:

通常在使用 React+Typescript 编写组件时,我想在其中一个道具上触发 useEffect 挂钩。例如,假设一个按钮在每次连续鼠标点击时执行不同的功能:

export function ActionButton({clickActions}: {clickActions: (() => void)[]}) {

  const [clicked, setClicked] = useState(0);

  useEffect(() => {
    if (clickActions.length > 0 && clickActions[clicked % clickActions.length]) {
      clickActions[clicked % clickActions.length]();
    }
  }, [clicked, clickActions])

  return <button onClick={() => setClicked(clicked => clicked + 1)}/>
}

在这种情况下,该组件的客户端需要注意,他们需要以某种方式防止 clickActions 在每次渲染时成为不同的实例。例如,它可以只是一个常量,也可以使用useMemo 来记忆。

是否有让我的客户意识到这一点的最佳做法?有没有办法在违反此规则时触发编译时错误?

注意:我意识到这种特殊情况可以在不使用useEffect 的情况下解决,但这只是一个说明模式的简单示例。我对解决这个特定问题不感兴趣,而是对如何解决一般问题感兴趣。

【问题讨论】:

    标签: reactjs typescript react-hooks


    【解决方案1】:

    您无法通过 TypeScript 技巧来强制 clickActions 不能在每次父渲染时更改。

    但是,您可以简单地从 useEffect 的依赖项列表中删除 clickActions。通常,在执行此操作时必须小心,但在这种情况下,它是安全的,因为您传递给 useEffect 的回调 同步clicked 更改时执行一个操作,这表示回调将在需要时引用最新的最近 clickActions

    这类似于 RxJS 中的 sample 运算符;即clickActionsclicked 采样。

    useEffect(() => {
      if (clickActions.length > 0 && clickActions[clicked % clickActions.length]) {
        clickActions[clicked]();
      }
    }, [clicked])
    

    还请注意,即使父级使用 useMemo,也不能保证在 React 选择时不会重新创建 clickActions

    你可以依赖useMemo 作为性能优化,而不是语义保证。 未来,React 可能会选择“忘记”一些以前记忆的值并在下次渲染时重新计算它们,例如为屏幕外组件释放内存。编写您的代码,使其在没有useMemo 的情况下仍然可以工作——然后添加它以优化性能。

    (见useMemo docs

    可以肯定地假设 useCallback 带有类似的警告,尽管文档中没有特别提及。

    【讨论】:

    • 感谢您对 useMemo 文档的引用。这很有趣,但也很奇怪,因为它似乎与 useCallback 的文档相矛盾:“useCallback 将返回回调的记忆化版本,只有在其中一个依赖项发生更改时才会更改。这在将回调传递给优化时很有用" (reactjs.org/docs/hooks-reference.html#usecallback)
    • 那么这是否意味着useCallback 也是如此?
    • @wvdz 你是对的。文档中存在矛盾,需要澄清。我不会看 React 源代码来澄清,因为 useMemo 的当前实现提供了我们被警告不要依赖的语义保证。
    • 哈哈,如果是这样的话,我的代码中有很多清理工作要做
    【解决方案2】:

    通常,如果您有一个正在传递其重要的函数(特别是如果它在函数内“创建”),则在创建它时使用useCallback。这样可以确保“道具不会改变”,从而防止重新渲染。

    parentFunction = useCallback(()=>{
      if (specialVar==='dance'){
        return () => {
          console.log('dance')
        }
      }
      else {
        return () => {
          console.log('do the boogy')
        }
      }
    }, [specialVar])

    【讨论】:

    • 感谢您的回答,但我认为这不能回答问题。我知道 useCallback 及其更通用的兄弟 useMemo。问题是:我如何让我的客户知道他们应该使用它?
    猜你喜欢
    • 2022-06-15
    • 2021-11-08
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 2023-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多