reactjsnext.jsuse-effectuse-stateasynchronous-javascript

How to make setting state with useEffect() to run on page refresh?


My code is not long or complicated at all. It's simple. so please read! (Im using react + next.js)

  1. In the root file, app.js, I have useEffect to fetch photo data. This data array will be used in a page component so I pass it down from app.js via <Component.../>
function MyApp({ Component, pageProps }) {
  const [photoData, setPhotoData] = useState([]);
  const [user, setUser] = useState([]);


  useEffect(() => {
    const getPhotos = async () => {
      try {
        const photoData = await axios.get(
          "https://jsonplaceholder.typicode.com/albums"
        );
        setPhotoData(photoData.data);
      } catch (error) {
        console.log(error);
      }
    };

    getPhotos();
  }, []);


useEffect(() => {
         //code for finding user. no external api used.
        setUser(user);
      }
    }
  }, []);


  const passedProps = {
    ...pageProps,
     photoData,
     user
  };

  return (
            ...
          <Component {...passedProps} /> 
   )
  1. Then I pass the data (photodata) from a Home component to a (app.js 's) grandchild component, an Photo component
export default function Home({ photoData, user }) {
 return(
          <Photo photoData={photoData} user={user} />
)

  1. In Photo component, I am receiving photoData and trying to set a state for photoArr with the default state of photoData. When the entire app is first loaded, the photoData is passed down to the Photo component successfully that it sets the state without any issue.
    But the main problem is that when I am in the Photo page (photos are loaded) and refresh the page, then it does not set the state for photoArr with photoData. Even though I can console log photoData received from app.js, it does not set state, photoArr, with the default state, photoData.
export default function Photo({ photoData, user }) {

  const [photoArr, setPhotoArr] = useState(photoData); 
//I have this as state because I change this array 
//later on in this component (doing CRUD on the photo array).
  

 console.log(photoData); // this returns the array received from app.js
 console.log(photoArr);  // [].   returns an empty array
 console.log(user); // returns the user object received from app.js.

return (
<div>
 {photoArr.length > 0 ? 
  .... code mapping photoArr 
  : "Data is empty"       //It returns this comment when I refresh the page
}
</div>
)

As you can see above, when I refresh the page, I get "Data is empty" meaning photoArr was not set even with the given default state. If I keep refreshing the page multiple times, it still shows a blank page. From my research, it's due to setting state being asynchronous? So then how can I fix this problem?


Solution

  • Try this: (In your Photo page)

    const [photoArr, setPhotoArr] = useState(null); 
    
    useEffect(() => {
        if(photoData.length) setPhotoArr(photoData) // If not empty, set the Arr
    },[photoData]} // We listen to photoData's change
    

    On page load, there aren't any data in your photoData, and as it pass down to Photo component, react remembers that state. But with useEffect listen to photoData's change, we can setPhotoArr once the getPhotos function got the data back.