user14587589
user14587589

Reputation: 459

State update doesn't re-render component in ReactJS

I have a component in which I have this useEffect:

  const [charactersInfo, setCharactersInfo] = useState(null);

  const [page, setPage] = useState(1);

  useEffect(() => {
    fetch(`https://api/api/character/?page=${page}`)
      .then((res) => res.json())
      .then((result) => {
        setCharactersInfo(result);
      });
  }, [page]);

whenever my page state updates there is different data coming from the api as expected. but issue is whenever new setCharactersInfo(result) happens, it does not display the new data.

I am passing my setPage state function to this component as a prop:

   <PaginationButtons
          data={charactersInfo}
          updatePage={(number) => {
            setPage(number);
          }}
        />

This is re-usable component which generates buttons and it works correctly everywhere except this specific component. any suggestions please?

import React, { useState, useEffect } from "react";

import "./PaginationButtons.css";

function PaginationButtons({ data, updatePage }) {
  const [buttonsArr, setButtonsArr] = useState([]);

  useEffect(() => {
    const finalArray = [];
    const { info } = data;
    // Not the best solution for situations in which
    // info.pages is big number(e.x 1000000) but since we know that
    // it mostly will be 34 or less so we can just loop through it :)
    for (let i = 1; i < info.pages + 1; i++) {
      finalArray.push(
        <button
          className="page_button"
          onClick={() => updatePage(i)}
          key={Math.random()}
        >
          {i}
        </button>
      );
    }
    setButtonsArr(finalArray);
  }, []);
  return <div className="button_container">{buttonsArr.map((el) => el)}</div>;
}

export default PaginationButtons;

data prop is an object which contains various of stuff and on the them is the number of pages that should be displayed. in this specific case it 34 for so I use state and useEffect to loop through this number and store buttons in the state array and map it afterwards

Upvotes: 1

Views: 130

Answers (3)

Farruh Akhrorov
Farruh Akhrorov

Reputation: 1

As stated in the other answers, you need to add data as a dependency. Also, you don't need to call map on buttonsArr, as you're not doing anything with its elements. Just use buttonsArr itself

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202595

The useEffect in PaginationButtons is using an empty dependency so it doesn't update when the data prop updates. From what I can tell you don't need the buttonsArr state anyway. It's also anti-pattern to store derived state from props. Just map the data array prop to the buttons.

Using random values is probably the least ideal method of specifying React keys. You can use the mapped array index, but you should use a unique property for each page element, or if there isn't one you should augment the data with a generated GUID.

function PaginationButtons({ data, updatePage }) {
  return (
    <div className="button_container">
      {data.?info?.pages?.map((page, i) => (
        <button
          className="page_button"
          onClick={() => updatePage(i)}
          key={i} // <-- or page.id if available
        >
          {i}
        </button>
      ))}
    </div>
  );
}

Upvotes: 1

Amruth
Amruth

Reputation: 5912

You should handle data change in your child component as well. pass data to useEffect dependency list.

 useEffect(() => {
    const finalArray = [];
    const { info } = data;
    // Not the best solution for situations in which
    // info.pages is big number(e.x 1000000) but since we know that
    // it mostly will be 34 or less so we can just loop through it :)
    for (let i = 1; i < info.pages + 1; i++) {
      finalArray.push(
        <button
          className="page_button"
          onClick={() => updatePage(i)}
          key={Math.random()}
        >
          {i}
        </button>
      );
    }
    setButtonsArr(finalArray);
  }, [data]);

This should help you, no need to maintain state. and i see pages is not array its just key value pair.

function PaginationButtons({ data, updatePage }) {
 const { info : { pages } } = data;
  return (
    <div className="button_container">
       <button
          className="page_button"
          onClick={() => updatePage(pages || 0)}
          key={pages}
        >
          {pages || 0}
        </button>
    </div>
  );
}

Upvotes: 1

Related Questions