Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 don't 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.

like image 268
dan_boy Avatar asked Mar 11 '21 20:03

dan_boy


People also ask

How can you prevent re-rendering a total component if it hasn't changed?

Memoization using useMemo() and UseCallback() Hooks Memoization enables your code to re-render components only if there's a change in the props. With this technique, developers can avoid unnecessary renderings and reduce the computational load in applications.

How do you prevent components from Rerendering?

If you're using a React class component you can use the shouldComponentUpdate method or a React. PureComponent class extension to prevent a component from re-rendering.

How do you stop a component from rendering even after state has been updated?

By default, when we call this method, the component re-renders once it receives new props, even though the props have not changed. To prevent the render method from being called, set the return to false, which cancels the render. This method gets called before the component gets rendered.

How do you stop a child component from Rerendering?

If you don't want a component to re-render when its parent renders, wrap it with memo. After that, the component indeed will only re-render when its props change.


3 Answers

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.. :)

like image 166
Keith Avatar answered Nov 16 '22 01:11

Keith


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.

like image 26
Nicholas Tower Avatar answered Nov 16 '22 02:11

Nicholas Tower


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>
  );
};
like image 37
PS-PARSA Avatar answered Nov 16 '22 01:11

PS-PARSA