'How to prevent re-rendering of components that have not changed?

I have a component that consists of several other components such as text fields, and when an input is made to the text field, all other components are re-rendered. I would like to prevent the re-rendering and only re-render the component that actually changes. I have seen that "useCallback" is the right way to do this and I have already seen how to use it. However, I'm having some trouble getting "useCallBack" to work correctly for my case. Even if I set it up in a simple manner as below, each new character entered into the text field causes the button to be rendered again. I dont see my mistake.

See working example in sandbox.

const Button = () => {
  console.log("Button Rendered!");
  window.alert("Button Rendered");
  return <button onClick="">Press me</button>;
};

export default function App() {
  const [textInput, setTextInput] = useState("Hallo");

  const onChangeInput = useCallback(
    (e) => {
      setTextInput(e.target.value);
    },
    [textInput]
  );

  return (
    <div>
      <input
        type="text"
        onChange={onChangeInput}
        value={textInput}
      />
      <Button />
    </div>
  );
}

I am happy for any calrification.



Solution 1:[1]

Personally I would avoid React.memo / React.useRef / React.useCallback.

The simplest solution to your example is just create another component, and store the state with this.

eg.

const Button = () => {
  console.log("Button Rendered!");
  window.alert("Button Rendered");
  return <button onClick="">Press me</button>;
};

const TextInput = () => {
  const [textInput, setTextInput] = useState("Hallo");
  const onChangeInput = useCallback(
    (e) => {
      setTextInput(e.target.value);
    },
    [textInput]
  );
  return (
      <input
        type="text"
        onChange={onChangeInput}
        value={textInput}
      />
  );
}


export default function App() {
  return (
    <div>
      <TextInput/>
      <Button />
    </div>
  );
}

In the above if you change Text, there is not State change in App, so Button doesn't get re-rendered, no need for useMemo etc..

You will find React works really well, the more you divide your components up, not only does it solve issues of re-render, but potentially makes it a lot easier to re-use components later.

IOW: keep state as close to the component as possible, and performance will follow.

Of course your example is simple, and in a real app you will have HOC's etc to cope with, but that's another question.. :)

Solution 2:[2]

useCallback does not prevent rerenders. React.memo is what prevents renders. It does a shallow comparison of the previous props with the new props, and if they're the same, it skips rendering:

const Button = React.memo(() => {
  console.log("Button Rendered!");
  window.alert("Button Rendered");
  return <button onClick="">Press me</button>;
});

The only role that useCallback plays in this is that sometimes you want to pass a function as a prop to a memoized component. For the memoization to work, props need to not change, and useCallback can help the props to not change.

Solution 3:[3]

changing the state causes re-render component along with all his heirs, to prevent re-render some sections, you can use useMemo to prevent unwanted re-rendering...

NOTE: useMemo has some costs... so don't overuse it (In this small example, it is not recommended at all).

in this case, if you do not need to re-rendering, you can use the useRef to save the input reference to get that value whenever you need it.

e.g:

const BlahBlah = () => {
  const inputRef = React.useRef(undefined);

  return (
    <div>
        <input ref={inputRef} />
        <button onClick={() => console.log(inputRef.current.value)}
    </div>
  );
};

Solution 4:[4]

I had similar problem. I wanted to avoid rendering a component which dependent on part2 of state, when only part1 has changed.

I used shouldComponentUpdate(nextProps, nextState) as described here https://reactjs.org/docs/optimizing-performance.html to avoid rendering it.

Solution 5:[5]

I'm using it this way:

const [state_data, set_state_data] = useState([
    true, // loading.
    {}, // vehicle.
    {} // user.
]);

This allows rendering only 1x instead of 3x and React keeps track of the changes. For some reason it won't track the change of objects, but arrays yes.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Keith
Solution 2 Nicholas Tower
Solution 3 PS-PARSA
Solution 4 prembhaskal
Solution 5 zyrup