0

I'm trying to set the id property of a state object based on the id attribute of the button I clicked, and then append it into a state array and then store it in the localstorage. but there are weird things happening in my code:

  1. when I clicked the 1st button, the console.log shows the id value of the 1st button (5) but the state object were still empty

  2. then I clicked on the 2nd button, the console.log shows the id value of the 2nd button (12) but the state object now contains the value from the 1st button (5)

  3. then I called the 3rd button, the console.log shows the id value of the 3rd button (100) but the state object contains the value of the 2nd button (12)

  4. why are the Buttons still look like a regular/default button even though I already added the react-bootstrap dependency, imported the Button component, and applied the variant="link" in the Button?

here's my code (and the codesandbox)

import { useState, useEffect } from "react";
import { Button } from "react-bootstrap";

export default function App() {
  const [cartList, setCartList] = useState([]);
  const [cartItem, setCartItem] = useState({});

  useEffect(() => {
    let localCart = localStorage.getItem("cartList") || "[]";
    console.log("localcart", localCart);
    if (localCart) {
      localCart = JSON.parse(localCart);
    }
    setCartList(localCart);
  }, []);

  const handleClick = (e, item) => {
    e.preventDefault();

    const arr = e.target.id.split("-");
    const selectID = arr[1];
    console.log("selectID", selectID);
    setCartItem({ ...cartItem, id: selectID });
    console.log("cartItem", cartItem);

    let itemIndex = -1;
    console.log("cartlist", cartList);
    for (let i = 0; i < cartList.length; i++) {
      if (cartItem && selectID === cartList[i].id) {
        itemIndex = i;
      }
    }
    if (itemIndex < 0) {
      console.log("added to cartList on index", itemIndex, cartItem);
      setCartList([...cartList, cartItem]);
      localStorage.setItem("cartList", JSON.stringify(cartList));
    }
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <Button
        variant="link"
        id="item-5"
        onClick={(e) => handleClick(e, cartItem)}
      >
        item no.5
      </Button>
      <Button
        variant="link"
        id="item-12"
        onClick={(e) => handleClick(e, cartItem)}
      >
        item no.12
      </Button>
      <Button
        variant="link"
        id="item-100"
        onClick={(e) => handleClick(e, cartItem)}
      >
        item no.100
      </Button>

      <div>
        {cartList.length > 0 ? (
          cartList.map((item, index) => <div key={index}>{item.id}</div>)
        ) : (
          <div>there is no data here</div>
        )}
      </div>
    </div>
  );
}
dapidmini
  • 1,490
  • 2
  • 23
  • 46
  • Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – pilchard Jun 18 '22 at 10:58

1 Answers1

2

The problems number 1, 2, 3 are all caused by the same reason: setCartItem works asynchronously so when you do this:

setCartItem({ ...cartItem, id: selectID });
console.log("cartItem", cartItem);

The value that you print in the console.log is still the old state because it doesn't wait for the setCartItem to complete the update. So, if you want to do a console.log of the new state each time it changes you can use instead a useEffect hook:

useEffect(() => {
 console.log("cartItem", cartItem); // this will be executed each time the cartItem has been changed
}, [cartItem])

About the question number 4, make sure to import the bootstrap css file:

/* The following line can be included in your src/index.js or App.js file*/

import 'bootstrap/dist/css/bootstrap.min.css';

More infos here in the official site: https://react-bootstrap.github.io/getting-started/introduction/#stylesheets

Hakim Abdelcadir
  • 1,231
  • 4
  • 15