Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React w/hooks: prevent re-rendering component with a function as prop

Let's say I have:

const AddItemButton = React.memo(({ onClick }) => {
  // Goal is to make sure this gets printed only once
  console.error('Button Rendered!');
  return <button onClick={onClick}>Add Item</button>;
});

const App = () => {
  const [items, setItems] = useState([]);

  const addItem = () => {
    setItems(items.concat(Math.random()));
  }

  return (
    <div>
      <AddItemButton onClick={addItem} />
      <ul>
        {items.map(item => <li key={item}>{item}</li>)}
      </ul>
    </div>
  );
};

Any time I add an item, the <AddItemButton /> gets re-rendered because addItem is a new instance. I tried memoizing addItem:

 const addItemMemoized = React.memo(() => addItem, [setItems])

But this is reusing the setItems from the first render, while

 const addItemMemoized = React.memo(() => addItem, [items])

Doesn't memoize since items reference changes.

I can'd do

 const addItem = () => {
   items.push(Math.random());
   setItems(items);
 }

Since that doesn't change the reference of items and nothing gets updated.

One weird way to do it is:

  const [, frobState] = useState();
  const addItemMemoized = useMemo(() => () => {
    items.push(Math.random());
    frobState(Symbol())
  }, [items]);

But I'm wondering if there's a better way that doesn't require extra state references.

like image 964
lxe Avatar asked Feb 13 '19 00:02

lxe


People also ask

How can we stop a component from re-rendering If props are same?

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.

Do components re-render when props change?

React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically.

How do I stop re-rendering lists in React?

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.


1 Answers

The current preferred route is useCallback, which is the same as your useMemo solution, but with additional possible optimizations in the future. Pass an empty array [] to make sure the function will always have the same reference for the lifetime of the component.

Here, you also want to use the functional state update form, to make sure the item is always being added based on the current state.

  const addItem = useCallback(() => {
    setItems(items => [...items, Math.random()]);
  }, []);
like image 146
kingdaro Avatar answered Oct 12 '22 02:10

kingdaro