React: Hooks performance feature-request: do not trigger re-render

Created on 29 Dec 2019  路  2Comments  路  Source: facebook/react

Do you want to request a feature or report a bug?
Something in between. First I thought I found a bug, then I thought maybe I am not "thinking in hooks". Then I thought maybe this is a feature to be implemented.

What is the current behavior?
Look at the custom hook and the component that uses it below:

function useTestHook() {
  const [_, setState1]: any = React.useState(1);
  const [state2, __]: any = React.useState(-1);

  React.useEffect(() => {
    setInterval(() => {
      console.log(' --- set interval running');
      setState1((current: number) => current + 1);
    }, 1000);
  }, []);

  return React.useMemo(() => state2, [state2]);
}

function TestComponent() {
  const state2 = useTestHook();
  console.log(' --- component re-rendering');

  return <button>state2: {state2}</button>;
}

What is the expected behavior?

First I expected my component not to re-render when setState1 under setInterval invoked.

Which versions of React, and which browser?

{
    "react": "^16.11.0",
    "react-dom": "^16.11.0",
}

And I thought useTestHook would keep re-invoking for itself.

I think I'll find it helpful to have such a thing to bypass custom hook invoking re-render for it's user.

Most helpful comment

I think this is a misunderstanding :smile:

First I expected my component not to re-render when setState1 under setInterval invoked.

The setState1 call above is telling React "update this state value and then re-render".

React does not know that your component doesn't actually make use of the _ variable.

React will "bail out" before rendering deeper though, since your render doesn't produce any new output, so the "cost" of the render will be small.

One related "performance" feature React does have in this regard though is to bail out if you return the same state value (so if you were returning current => current rather than current => current + 1).

return React.useMemo(() => state2, [state2]);

Also just wanted to point out that this won't have any effect. If the thing you're returning is just a primitive, like a number, then there's no need to memoize it. This type of memoization is typically used to make objects with many keys/values stable between render (since recreating the object wrapper would normally bypass equality checks).

All 2 comments

I think this is a misunderstanding :smile:

First I expected my component not to re-render when setState1 under setInterval invoked.

The setState1 call above is telling React "update this state value and then re-render".

React does not know that your component doesn't actually make use of the _ variable.

React will "bail out" before rendering deeper though, since your render doesn't produce any new output, so the "cost" of the render will be small.

One related "performance" feature React does have in this regard though is to bail out if you return the same state value (so if you were returning current => current rather than current => current + 1).

return React.useMemo(() => state2, [state2]);

Also just wanted to point out that this won't have any effect. If the thing you're returning is just a primitive, like a number, then there's no need to memoize it. This type of memoization is typically used to make objects with many keys/values stable between render (since recreating the object wrapper would normally bypass equality checks).

Yeay!
Thanks for putting your time and explaining this.

Was this page helpful?
0 / 5 - 0 ratings