0

I am calling a standard JSON file using fetch on a React Hooks project. (I'm mocking a server using JSON-Server to read the local json file)

In my main function, I'm calling a variable and a function, like any normal React Hook, and setting it to an empty array because I'm calling API data using fetch.

let [items, setItems] = useState([]);

Then I'm making my actual fetch call to my mock server

    const fetchItems = async () => {
        const url = "http://localhost:3002/learn";
        const response = await fetch(url);
        items = await response.json();
        setItems(items);
        console.log(items[0].title);
    }

The console.log(items[0].title) does in fact return the correct value.

If I wanted to display all results, the following would even work.

{items.map((item) => 
   <img src={item.source} alt={item.title} title={item.title}  className="img-fluid mx-auto d-block" />
)}

However, once I want to display a single value in my return, I get an error message saying: "TypeError: Cannot read property 'source' of undefined" The code that I'm using, just to test at first is the same exact thing as my console.log from before.

<img src={items[0].source} alt={items[0].title} title={items[0].title}  className="img-fluid mx-auto d-block" />

Am I missing a major step that allows me to use the fetched info in my return?

If I were to hard-code my JSON data on the same component, it would work. I feel like it has something to do with fetch. I was looking at this question, and it's similar, but not the same thing. I'm also using hooks and also not axios. Can't display data after fetch

UPDATE: The JSON file looks something like this:

{
    "learn": [
      {
        "id": 1,
        "title": "Angular JS",
        "source": "./images/logo-angular-js.png"
      },
      {
        "id": 2,
        "title": "Firebase",
        "source": "./images/logo-firebase.png"
      },
      {
        "id": 3,
        "title": "GraphQL",
        "source": "./images/logo-graphql.png"
      }
    ]
}
6
  • Please share the items array. What it looks like? Is there anything called source? Commented May 8, 2020 at 21:43
  • 1
    items = await response.json(); don't do this. Only set state using the state setter. Do not mutate state directly. Commented May 8, 2020 at 21:45
  • The only reason I did that was because it was saying my result was undefined if I did it like let items = await response.json(); Commented May 8, 2020 at 22:52
  • Try items = (await response.json()).learn; in your fetchItems call. Commented May 8, 2020 at 23:08
  • If that's not helping, try logging the entire items object in the fetch call and posting its contents here. It might be that the array contains at least one null value, apart from the first item that you are currently logging. Commented May 8, 2020 at 23:11

1 Answer 1

2

On your first render, items has a length of 0, so items[0] is going to be undefined. You're likely trying to render before that first item has loaded.

Check if items[0] is defined before trying to access items[0].source and the like.

function YourComponent() {
  const [items, setItems] = useState([]);

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

  const fetchItems = async () => {
    const url = "http://localhost:3002/learn";
    const response = await fetch(url);
    items = await response.json();
    setItems(items);
    console.log(items[0].title);
  }

  if (!items.length) {
    return null;
  }

  return (
    <img 
      src={items[0].source}
      alt={items[0].title}
      title={items[0].title}
      className="img-fluid mx-auto d-block" /> 
  );
}

On the first render, this will cause nothing to be output. After the setItems call is made, the component will re-render, and this time (if there were items being set), it'll render your item.

Sign up to request clarification or add additional context in comments.

3 Comments

Damn, this does work. Ok, just so I know what's going on. If making a fetch call, the component renders first, then on the old componentDidMount(useEffect), the fetch call happens, which re-renders. That would make sense for why hard coded objects would work right away and not fetched info. Hard coded info is rendered immediately with the original render. This really feels like a benchmark. Thank you so much. I was really losing faith in StackOverflow.
Would you have an explanation as to why .map works then? Is there a built-in continue feature in .map?
It's because doing a map over an empty array makes another empty array, and rendering an empty array of elements works has the effect of not rendering anything.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.