Home > Software design >  Child Components not being updated after updating context React
Child Components not being updated after updating context React

Time:01-01

I'm trying to create a cart mechanism using react context.

This is my context provider

export default function App() {
  const [cart, setCart] = useState(cartInitialState);

  return (
    <CartContext.Provider value={{ cart, setCart }}>
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
      </div>
      <MenuItemDetailsView />
    </CartContext.Provider>
  );
}

Where cart initial state is some dummy empty cart. The MenuItemDetailsView is a child view that uses the parent component context

function MenuItemDetailsView() {
  const { cart, setCart } = useContext(CartContext);

  const findItemInCart = () => {
      //return quantity of cart item with specific id
  };

  const handleAdd = () => {
    const itemIndex = cart.items.findIndex((item) => item.id === 0);

    let newCart = cart;
    console.debug(newCart);
    if (itemIndex === -1) {
      //do stuff
    } else {
      //do stuff
    }
    setCart(newCart);
  };

  return (
    <div>
      <h1>{findItemInCart()}</h1>
      <button onClick={handleAdd}>add</button>
    </div>
  );
}

The child view should be able to update the context, which it does correctly. The problem is that the parent App is not rerendering the MenuItemDetailsView component after updating the context. (The App component rerender itself

Here is a sandbox of my code

https://codesandbox.io/s/react-context-demo-cart-eyjx2

I'm not quite understanding the problem here. Shouldn't the App component rerender its children too? If not, how can I tell him to rerender when updating the context?

I understand the possibility to set a local child variable and update both the variable and the context itself when updating the quantities but I feel that would be a workaround to the real problem.

SN: When exiting the MenuItemView and reentering it (from a multipage app, not displayed on sandbox) the quantity gets updated, that means that useContext is correctly working and that the cart item is being correctly read every time the MenuItemView is being rerendered. But again I want that the cart gets updated without getting out of the component and getting back in manually

CodePudding user response:

The issue here is that you're mutating the cart state.

let newCart = cart;

The newCart is still referencing the same object that you keep in state, so when you update newCart, you are mutating cart as well. Once you do setCart(newCart), React bails out of the state update (and rerender) because it's the same object as before. There is more info on how React compares objects when updating state in the React docs.

The solution here is to copy the state first as a new object:

const newCart = { ...cart };

or better yet, use the functional state update:

setCart(prevCart => {
  const newCart = { ...prevCart };
  // do stuff with newCart
  return newCart;
});
  • Related