Liiaam93
Liiaam93

Reputation: 285

JS/React useState value not updating before function call?

This might be very simple JS, but can't seem to get my head around it.

The nextPage and prevPage functions/buttons seem to call the loadNewProducts function before updating the pageNum state. When i click 'next' it changes state of pageNum to '2' but loads page 1. Then i click previous and it changes state of pageNum to '1' but loads page 2...

const [pageNum, setPage] = useState(1);
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(false);

  const loadNewProducts = async () => {
    setLoading(true);
    const req = await fetch(`/api/nhan/${pageNum}`);
    const json = await req.json();
    setProducts(json);
    console.log(products);
    setLoading(false);
  };

const nextPage = () => {
    setPage(pageNum + 1);
    loadNewProducts();
  };
  const prevPage = () => {
    setPage(pageNum - 1);
    loadNewProducts();
  };
  if (pageNum <= 0) {
    setPage(1);
  }
  return (
    <>
      <NavBar />
      <div>
        <Head>
          <title>Create Next App</title>
          <meta name="description" content="Generated by create next app" />
          <link rel="icon" href="/favicon.ico" />
        </Head>
        <button onClick={loadNewProducts}> Load </button>
        <button onClick={prevPage}>Previous</button>
        <button onClick={nextPage}>Next</button> Page {pageNum}
        {loading && <div>LOADING</div>}
        <NewProducts newProducts={products} />
      </div>
    </>
  );
}

Upvotes: 0

Views: 1089

Answers (3)

Pradip Patil
Pradip Patil

Reputation: 104

setPage is a state updating function which is asynchronous in nature. So instate of using setPage(pageNum + 1) , which might pick up the old state, you should you another overload of the state updating function where you receive old state as an argument. like below

 const nextPage = () => {
    setPage(pageNum => pageNum + 1);
  };

  const prevPage = () => {
    setPage(pageNum => pageNum - 1);
  };

Upvotes: 1

Viet
Viet

Reputation: 12777

Because setState is async. It only has new value when component re-render. So you should use useEffect in this case:

  const nextPage = () => {
    setPage(pageNum + 1);
  };

  const prevPage = () => {
    setPage(pageNum - 1);
  };

  useEffect(() => {loadNewProducts()}, [pageNum])

Upvotes: 1

Quentin
Quentin

Reputation: 943207

Calling setPage will asynchronously update the state and trigger a rerender.

This will cause the component function to run again and, when it does, pageNum will be assigned a different value from the state by useState.

However, first the nextPage function will continue to run having closed over the old value of pageNum.


To handle this, use a useEfect hook with a dependency on pageNum to run loadNewProducts.

const nextPage = () => setPage(pageNum + 1);
const prevPage = () => setPage((pageNum - 1) || 1); // This stops you setting it to 0 in the first place

useEffect(() => {
    const loadNewProducts = async () => {
        setLoading(true);
        const req = await fetch(`/api/nhan/${pageNum}`);
        const json = await req.json();
        setProducts(json);
        setLoading(false);
      };
      loadNewProducts();
}, [pageNum, setLoading, setProducts]);

useEffect(() => {
    console.log(products);
}, [products]);

Upvotes: 4

Related Questions