0

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>
    </>
  );
}

Liiaam93
  • 195
  • 1
  • 2
  • 10
  • setstate is async. So your page number is updating after the load function is called. This may help you https://stackoverflow.com/questions/53898810/executing-async-code-on-update-of-state-with-react-hooks – RABI Aug 02 '21 at 07:29

3 Answers3

4

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]);
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Thanks! This worked perfectly, i've never used useEffect before and wasn't 100% how to, so this helps me a lot. – Liiaam93 Aug 02 '21 at 07:40
1

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])
Viet
  • 12,133
  • 2
  • 15
  • 21
1

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);
  };
Pradip Patil
  • 104
  • 4