Tim Taylor
Tim Taylor

Reputation: 81

How to push to state in React Hooks?

I am trying to update the state with an array. But I need to keep the existing information in the array and just push more information into it.

Here is my state:

  const [filteredProducts, setFilteredProducts] = useState([]);

This is my function I am calling for the onClick event listener

const filteredByCategory = (event, category, products) => {
  const element = document.getElementById(event.target.id);
  
  if (element.checked) {
    const productsToAdd = products.filter(
      (product) => product.category === category
    );
    
    setFilteredProducts((currentFilteredProducts) => [
      ...currentFilteredProducts,
      ...productsToAdd,
    ]);
    
    dispatch({
      type: "PRODUCTS_FILTERED",
      payload: filteredProducts,
    });
  } else {
    let removedCheckedProducts = filteredProducts.filter((product) => {
      return product.category !== category;
    });
    
    setFilteredProducts(removedCheckedProducts);
    
    if (removedCheckedProducts.length >= 1) {
      dispatch({
        type: "PRODUCTS_FILTERED",
        payload: removedCheckedProducts,
      });
    } else {
      dispatch({
        type: "PRODUCTS_FILTERED",
        payload: allProducts,
      });
    }
  }
};

Upvotes: 1

Views: 143

Answers (2)

Thomas Burke
Thomas Burke

Reputation: 1365

digitalbreed identified the race condition correctly in his answer. An example of this behavior can be seen in this code sandbox by viewing the console after clicking the addProduct button

Upvotes: 0

digitalbreed
digitalbreed

Reputation: 4070

My assumption is that you expect the filteredProducts state value to reflect your changes right after setFilteredProducts:

setFilteredProducts((currentFilteredProducts) => [
    ...currentFilteredProducts,
    ...productsToAdd,
]);
dispatch({
    type: "PRODUCTS_FILTERED",
    payload: filteredProducts,
});

Unfortunately, that's not how state updates work. The set* function simply queues the update for the next render, but does not immediately update the state.

You could maintain a local variable to pass the update both into the local state and into the Redux store:

const updatedProducts = [
    ...filteredProducts,
    ...productsToAdd,
];
setFilteredProducts(updatedProducts);
dispatch({
    type: "PRODUCTS_FILTERED",
    payload: updatedProducts,
});

Upvotes: 2

Related Questions