React Hooks - 如何实现 shouldComponentUpdate?

React Hooks - How do I implement shouldComponentUpdate?

我知道你可以通过传递一个数组作为可选的第二个参数来告诉 React 跳过一个 effect。

例如:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

但是如果我想控制比较呢?添加我自己的比较逻辑。

我希望您可以像 React.memo 那样将函数作为第二个参数传递。

如果传入数组的 属性 发生变化,则传递可选的第二个数组参数会启动效果函数 - 就像 class 组件中的 shouldComponentUpdate 方法 运行 当传递给组件的道具发生变化时。您可以根据参数的值在效果函数中决定是否要应用效果。

这是一个自定义挂钩,它接受一个更新函数,returns一个只有当更新函数returns为真时才会改变的值,可以在第二个参数中传递给[=13] =] 或 useCallbackuseMemo 强制 re-render:

function useShouldRecalculate(shouldRecalculateFn) {
  const prevValueRef = useRef(0);
  if(shouldRecalculateFn()) {
    // If we need to recalculate, change the value we return
    prevValueRef.current += 1;
  } // else we return the same value as the previous render, which won't trigger a recalculation
  return prevValueRef.current;
}

例如,这将仅在计数为偶数时更新文档标题:

const shouldUpdateTitle = useShouldRecalculate(
  () => count % 2 === 0
);

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [shouldUpdateTitle]); // eslint-disable-line react-hooks/exhaustive-deps

为什么你可能不应该这样做

在大多数情况下,我不建议这样做。

一般来说,使用惯用的挂钩 API 将有更简洁的方法来完成相同的任务。 (上面的示例可能只是在更新文档标题的行周围放置了一个 if 块。)

也许更重要的是,deps 论点不仅仅是关于优化,而是关于保持闭包值 up-to-date,并避免像这样的错误:

const [count, setCount] = useState(0)
const increment = useCallback(() => {
    // Bug: `count` is always 0 here, due to incorrect use of the `deps` argument
    setCount(count + 1)
}, [])

此错误将被 react-hooks/exhaustive-deps linter 规则捕获,但您必须在使用自定义逻辑控制执行的任何地方禁用该规则。

使用自定义记忆化逻辑可能会使您的组件更 bug-prone 并且通常更难推理。所以我认为这个 useShouldRecalculate 挂钩是最后的手段。

在上面的评论中 Gabriele Petrioli 链接到解释如何实施 shouldComponentUpdate 的 React.memo 文档。我在谷歌上搜索了 shouldComponentUpdate + useEffect + "react hooks" 的组合,结果是这个。因此,在使用链接文档解决了我的问题后,我想我也应该将这些信息带到这里。

这是实现 shouldComponentUpdate 的旧方法:

class MyComponent extends React.Component{
  shouldComponentUpdate(nextProps){
    return nextProps.value !== this.props.value;
  }
  render(){
    return (
     <div>{"My Component " + this.props.value}</div>
    );  
 }
}

新的 React Hooks 方式:

React.memo(function MyComponent (props) {

  return <div>{ "My Component " + props.value }</div>;

}) 

我知道您可能在问题中提出了更多要求,但是对于来自 Google 的任何正在寻找如何使用 React Hooks 实施 shouldComponentUpdate 的人来说,您就可以了。

文档在这里: how-do-i-implement-shouldcomponentupdate

另一种方法可能是使用 useRef 来保存数据,而仅使用 useState 来存储要显示的数据。 有时这比备忘录方法更有效:我最近有一个案例,当使用 React.memo 时,React 仍在不必要地渲染,并且它弄乱了一些 PIXI 显示。 下面的方法为我修复了它......希望我没有做反模式:-)

const countRef = useRef(0);
const [countDisplay, setCountDisplay] = useState(0);

yourUpdateFunction = () => {
  // This is where count gets updated
  countRef.current = countRef.current + 1;
  if ((countRef.current % 2) === 0) setCountDisplay(countRef.current);
}

return (<p>countDisplay</p>);

添加到 PAT-O-MATION 的回答中,
React.memo 还接受第二个参数,这是一个函数,可以用来确定一个组件是否 应该渲染还是不渲染。

如果函数 return 为真,则组件不会在更改该道具时重新渲染,相反,当 return 值为假时它会更新

function SomeComp({prop1, prop2}) {
    return(
        ..
    )

}
React.memo(SomeComp, (props, nextProps)=> {
    if(props.prop1 === nextProps.prop1) {
        // don't re-render/update
        return true
    }
})

注意:只有当回调函数return为false时组件才会重新渲染,所以在上面的例子中即使prop2值改变它也不会重新渲染

除了 Avinash 的 回答。 返回值的重要说明:

shouldComponentUpdate() {
  // returns true by default
  // return false if you don't need re-render
}

export default React.memo(Component, (props, nextProps) => {
  if(props.prop1 === nextProps.prop1) {
    // return true if you don't need re-render
  }
})