Anargyros Stylidis
Anargyros Stylidis

Reputation: 279

UseState loads previous state when value changes (NextJS)

I am creating an e-commerce app and on the Shop page I want to implement filters based on category and price. I have set the state like so:

const [filters, setFilters] = useState({
  cat: "",
  price: "",
});        

And this is the select tag and the options to trigger the change for the category:

<select className="outline-none" name="category" onChange={handleChange}>
  <option value="start">-</option>
  <option value="Hoodie">Hoodie</option>
  <option value="T-Shirt">T-Shirt</option>
  <option value="Sweatshirt">Sweatshirt</option>
</select>

This is the handleChange method:

const handleChange = (e) => {
  const value = e.target.value;
  if (value === "start") {
    setProductsF(products);
  } else {
    setFilters((prevFilters) => ({ ...prevFilters, cat: value }));
    const testArr = [...products];
    const filteredProd = testArr.filter(
      (prod) => prod.categories[0].name === filters.cat
    );
    setProductsF(filteredProd);
  }
}

The problem is, when the handleChange fires the filters get the previous value and are not updated correctly. Let's say I change the values to "Hoodie" then "T-Shirt". When I click on "Hoodie" the shopping list is empty and when I click on "T-Shirt", I get the list of hoodies. I get the products from static props and I am setting the filtered products on this state:

const [productsF, setProductsF] = useState(products);

What is the way to get the correct filters values when the user selects an option?

Upvotes: 1

Views: 2707

Answers (1)

Zsolt Meszaros
Zsolt Meszaros

Reputation: 23161

You try to use the updated filters state right after you "updated" it. Don't forget that useState is asynchronous just like setState in class components. You can't update the state on one line and assume it's already changed on the next one. You'll likely use the unchanged state.

Instead of filters.cat, you can use the value you receive. This way you update both the filters and the productsF state.

const handleChange = (e) => {
  const value = e.target.value;
  if (value === 'start') {
    setProductsF(products);
  } else {
    setFilters((prevFilters) => ({ ...prevFilters, cat: value }));
    const testArr = [...products];
    const filteredProd = testArr.filter(
      (prod) => prod.categories[0].name === value,
    );
    setProductsF(filteredProd);
  }
};

Upvotes: 2

Related Questions