Search code examples
javascriptarraysreactjsasynchronousfetch

Flaky errors when fetching & rendering data using React Hooks


I'm fetching data from an API & rendering the results. This is working for a while & then I'm getting the following error: itemList.js:23 Uncaught TypeError: items.map is not a function & also Consider adding an error boundary to your tree to customize error handling behavior.

Any ideas on why is this happening?

This is my code sample:

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

const ItemList = () => {
    const [items, setItem] = useState("");

    useEffect(() => {
        const url = "<redacted-api-endpoint>";

        const fetchData = async () => {
            try {
                const response = await fetch(url);
                const json = await response.json();

                return setItem(json.data);
            } catch (error) {
                console.log("error", error);
            }
        };

        fetchData();
    }, []);

    return (
        <>
            {
                items.map((item, index) => {
                    const { name, title, description, image, price } = item;

                    return (
                        <div key={index}>
                            <img src={image} alt="item" width="100" height="auto" />
                            <p>{name}</p>
                            <p>{title}</p>
                            <p>{description}</p>
                            <p>{price}</p>
                        </div>
                    )
                })
            }
        </>
    );
};

export default ItemList;



Solution

  • useEffect(callback, dependencies) is for side-effects hook that runs after the component is mounted, it manages the side-effects in functional components. callback argument is a function to put the side-effect logic. dependencies is a list of dependencies of your side-effect: being props or state values. useEffect(callback, dependencies) invokes the callback after initial mounting, and on later renderings, if any value inside dependencies has changed.

    In your first/initial render, the component is mounted with it's initial state which is const [items, setItem] = useState(""); and you are maping over an empty string.

    Solution:

    consider adding short circuiting, or add item in your initial state to prevent the error.

        <>
                {
                    items && items.map((item, index) => {
                        const { name, title, description, image, price } = item;
    
                        return (
                            <div key={index}>
                                <img src={image} alt="item" width="100" height="auto" />
                                <p>{name}</p>
                                <p>{title}</p>
                                <p>{description}</p>
                                <p>{price}</p>
                            </div>
                        )
                    })
                }
            </>
    

    take a quick look at official react docs concerning useEffect hook