0

I was experimenting with React a little and wanted to retrieve a json file I had on S3 bucket. I'm using the next code

import React, { useState, useEffect } from "react";
import { Storage } from "aws-amplify";

export default function Gallery(props){
  const [picList, setPicList] = useState([]);

  useEffect(() => {
    async function onLoad() {
      try{
        const picUrl = await Storage.get('list.json');
        const picr = await fetch(picUrl);
        const picjs = await picr.json();
        setPicList(picjs);
        console.log(picList);
      }
      catch(e){
        alert(e);
      }
    };
    onLoad();
  }, [picList]);



  function renderLander() {
    return (
      <div className="lander">
        <h1>Lander</h1>
      </div>
    );
  }

  return(
    <div className="Gallery">
      {renderLander()}
    </div>
  );
}

In the begining I was getting the error that when using setPicList, picList doesn't take the value of json file. I'm using console.log() to debug, so I'm sure that Storage.get is getting the right url, fetch() is getting the file json, but picList prints the default value [].

Now, with the code above instead I'm getting an infinite loop, but this time the value of picList is getting the right value (but it keeps fetching S3 for every render I think).

I was following this guide

Edit: for completeness, picList is not getting updated if I remove the dependency, ie if I let [] instead of [picList].

2 Answers2

2

As Brian mentioned before, your useEffect is set to trigger whenever picList is changed. Since picList is being changed inside useEffect, this creates an infinite loop.

Since you're retrieving an object from S3, all you need to do is retrieve it once.

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

Ensures that the useEffect will only run once, when first rendered

React hooks changes states asynchronously, so console.logging right after will produce either None or the previous value.

What you need to do is add a conditional statement to your render function, to re-render the component when the state updates.

function renderLander() {
    return (
      <div className="lander">
        <h1>Lander</h1>
      </div>
    );
  }

  return(
    <div className="Gallery">
      {picList ? renderLander() : null}
    </div>
  );
alex067
  • 3,159
  • 1
  • 11
  • 17
  • Thanks you. I have update my question, as I tried this on the begining but the `picList` variable was left unchanged from its default value. –  Dec 02 '19 at 21:26
  • Hooks in react update asynchronously, so if you console.log(picList) right after, you'll get None. – alex067 Dec 02 '19 at 21:28
  • I see. Maybe I should make another question, but I hope my problem has a easy solution: what little change can I make to use the data from `picList` in my `renderLander()`? I tried to put `{picList.title}` but it gets undefined. Instead, if I force an error (like writing `

    {picList}

    `) the error prints the full json data.
    –  Dec 02 '19 at 21:34
  • View my edit for a possible solution, let me know if it works! – alex067 Dec 02 '19 at 21:43
  • Many thanks but no luck. I tried setting if(picList === []){onLoad();} but doesn't render the value inside the return. It's getting tricky. I just need to get json from a file. –  Dec 02 '19 at 21:54
  • Are you sure you're getting anything from s3? – alex067 Dec 02 '19 at 22:09
  • Yes, I can see the file while inspecting with the chrome console (in network tab I see the fetched file with the headers for authentication, then inspecting the response I get the actual content of the file). My suspect is that it renders before fetching, then it doesn't update the value after fetching. –  Dec 02 '19 at 22:12
  • Thanks for clarifying. Check my re-edit for a possible solution – alex067 Dec 02 '19 at 22:29
0

You call setPicList in the callback function that listens for side effects off picList:

useEffect(() => {
    setPicList(someValue); //infinite loop because this causes a side effect
}, [picList]);

You are listening for changes to picList with useEffect and than make changes to picList when there is a change to picList in your useEffect call back function.

Brian Ogden
  • 18,439
  • 10
  • 97
  • 176