简体   繁体   English

React:如何避免使用钩子重新渲染组件,该钩子始终返回相同的值但会改变其内部状态

[英]React: how to avoid re-rendering a component with a hook that returns always the same value but changes its inner state

imagine to have this hook that changes his value every second and returns true if the random value is a multiple of 5 otherwise false.想象一下,这个钩子每秒都会改变他的值,如果随机值是 5 的倍数,则返回 true,否则返回 false。 what can I do to stop re-rendering every second?我该怎么做才能停止每秒重新渲染?

PS: I tried useMemo, and useRef to return back always the same object but it's still re-rendering. PS:我试过 useMemo 和 useRef 总是返回相同的对象,但它仍在重新渲染。

please help请帮忙

 const useRandomIs5x = () => { const [state, setState] = useState(0); useEffect(() => { const t0 = setInterval(() => { setState(getRandomValue()) }, 1000) return () => clearInterval(to) }, []) return state % 5 === 0; } const Root = () => { const bool = useRandomIs5x(); console.log("I'm re-rendering every second", bool) return <div>test</div> }

I believe this is barely doable without some work-arounds .我相信如果没有一些变通办法,这几乎是不可能的。 To call setState or not, we have to access the current state value.无论是否调用setState ,我们都必须访问当前状态值。 This is possible if we pass state in the dependency array.如果我们在依赖数组中传递state ,这是可能的。 But then, the interval will be re-created with every second.但是,间隔将每秒重新创建。

It could be also doable with refs, however there's no a correct way (yet) to listen to ref changes.它也可以用 refs 来实现,但是(目前)还没有正确的方法来监听ref 的变化。

Update : It looks like it works fine with usage of useRef as the previous data holder.更新:看起来使用useRef作为以前的数据持有者可以正常工作。 Thanks to anhau2.感谢 anhau2。

 const { useEffect, useState, useRef } = React; const useRandomIs5x = () => { const [state, setState] = useState(true); const ref = useRef(null); useEffect(() => { const t0 = setInterval(() => { const value = Math.floor(Math.random() * 5) % 5 === 0; if (value === ref.current) return; ref.current = value; setState(value); }, 1000); return () => clearInterval(t0); }, []); return state; } const Root = () => { const bool = useRandomIs5x(); console.log('re-render!', bool); return <div>test</div> } ReactDOM.render(<Root />, document.getElementById("root"));
 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div>

PS Looking forward for any other ideas how to deal with it. PS期待如何处理它的任何其他想法。

You probably need to approach it a bit differently.您可能需要以不同的方式处理它。 You don't want the random value, you want whether it's divisible by 5. So that's what should be in state and returned from the hook.你不想要随机值,你想要它是否可以被 5 整除。所以这就是状态和钩子返回的内容。 The random value can just be a ref.随机值可以只是一个引用。 So try something like this (code not tested but should give you the general idea):所以尝试这样的事情(代码未经测试,但应该给你一个大致的想法):

const useRandomIs5x = () => {
    const [divisibleBy5, setDivisibleBy5] = useState(true);
    const randomValue = useRef(0);

    useEffect(() => {
        const t0 = setInterval(() => {
            // Set the new random value into a ref so as to not cause re-render
            randomValue.current = getRandomValue();

            const randIsDivisibleBy5 = randomValue.current % 5 === 0;

            // Only if it changes do we update the boolean state and trigger re-render
            if (randIsDivisibleBy5 !== divisibleBy5) {
                setDivisibleBy5(randIsDivisibleBy5);
            }
        }, 1000);
        
        return () => clearInterval(to);
    }, []);

    // Return the boolean state value instead
    return divisibleBy5;
}

We better change the state to store is5x instead of the value.我们最好将状态更改为存储 is5x 而不是值。 One possible solution is to use useRef to check whether we should update the value every second.一种可能的解决方案是使用 useRef 来检查我们是否应该每秒更新该值。 And then, sync the useRef and the state value.然后,同步 useRef 和状态值。

const useRandomIs5x = () => {
  const [state, setState] = useState(false);
  const prevIs5x = useRef(false);
  useEffect(() => {
    const t0 = setInterval(() => {
      const is5x = getRandomValue() % 5 === 0;
      const isDiff = is5x !== prevIs5x.current;
      prevIs5x.current = is5x;
      if (isDiff) {
        setState((prevState) => !prevState);
      }
    }, 1000);
    return () => clearInterval(t0);
  }, []);
  return state;
};

I could fix that issue using this library react-hooks-in-callback isolating the hook from the component.我可以使用这个库react-hooks-in-callback将钩子与组件隔离来解决这个问题。

check this sandbox example检查这个沙箱示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM