1

I need to change the checkbox checked status, but useCallback re-renders component a lot of times. I don't understand how it works. I've read a lot of materials about it.

const AppealsList = () => {
  const [isFiltered, setFiltered] = React.useState(false);
  console.log('rerender');

  const changeCheckBox = useCallback(() => {
    setFiltered(!isFiltered);
  }, [isFiltered]);

  return (
    <div className={classNames('appeals')}>
      <div className={classNames('appeals__filter')}>
        <input
          checked={isFiltered}
          className={classNames('appeals__input')}
          type="checkbox"
          readOnly={true}
        />
        <label
          onClick={changeCheckBox}
          className={classNames('appeals__filter-label')} />
      </div>
    </div>
  );
};

Amount of re-renders:

Amount of re-renders

Barak
  • 1,390
  • 15
  • 27
MuffinColor
  • 21
  • 1
  • 5
  • What *do* you understand about it so far? – tadman Feb 09 '21 at 20:38
  • Why it's rerendering? I think that I don't use callbacks properly. And I wanna know how to use them in this case. – MuffinColor Feb 09 '21 at 20:43
  • `Why it's rerendering?` Presumably because you clicked the label. To get 15 renders, i'd expect you did 14 clicks (13 in strict mode). `useCallback` doesn't stop your component from rendering when its state changes. In some cases useCallback may let a *child* component skip a render, though that's not being used in your code. – Nicholas Tower Feb 09 '21 at 20:49
  • A render is different than something changing in the output HTML. That's part of the efficiency of react, rendering the component is separated from the slow operation of updating the DOM. Any change to component state or props, or your component re-mounting because something changes in the parent, or multiple components on the page, will cause renders to execute. – Andy Ray Feb 09 '21 at 20:49
  • Also note that in dev mode, if you're using strict mode, components will render twice https://stackoverflow.com/questions/61254372/my-react-component-is-rendering-twice-because-of-strict-mode – Andy Ray Feb 09 '21 at 20:50

2 Answers2

1

This is because you have written your callback with a dependency on isFiltered so it will change every time you click. It works best when it does not depend on the very state that the callback is changing.

To create a toggle function that does not constantly change, have it call setState with a function so that it can compute new state from previous state, taking advantage of functional state updates offered by useState:

const changeCheckbox = useCallback(() => {
    setFiltered(currentValue => !currentValue);
}, []); // look no dependencies!

You can read about functional state updates (deriving new state from current state) here: https://reactjs.org/docs/hooks-reference.html#functional-updates

If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.

Brandon
  • 38,310
  • 8
  • 82
  • 87
  • Ok. Thank you. But it doesn't disable rerender when I change the checkbox state. I want to change the checkbox checked status without rerenders. Am I thinking wrong? – MuffinColor Feb 10 '21 at 11:09
0

setCallback is used to memoize functions so if you have heavy functions that require a lot of processing you don't have to processes the same inputs twice. For example if you had a function that adds two numbers and you give it 4 and 3 then it runs the function and memorizes that when you input 4 and 3 into the function the result is 7. So next time it is passed 4 and 3 instead of processing the result it uses the memorized result.

While useCallBack is used to memorize functions, useEffect is used to prevent unnecessary rerenders. I think in this case you are wanting to use the useEffect Hook?

Colin Hale
  • 824
  • 5
  • 11
  • 1
    Your answer seems to be mixing some things up. `useCallback` doesn't memoize the *execution* of the function, just the *reference* of the function. It can improve performance, but not because it skips executing the function; it's because other components can see that the function didn't change, and so they can skip rerendering. Additionally, `useEffect` doesn't really have anything to do with preventing unnecessary rerenders. – Nicholas Tower Feb 09 '21 at 20:56