31

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.

1 Answer 1

22

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()]);
  }, []);
Sign up to request clarification or add additional context in comments.

4 Comments

"useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs)." which is what I did. But because items reference changes due to spread/concat, it doesn't get memoized
Well, here, you can change [items] to [] and it'll still work, since the update depends on previous state. I'll edit my answer.
I think the key here is to use a function in setItems! Thanks @kingdaro
Yep, that'll do it :P Sure thing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.