0

I've been working on that React Project and I've been using axios to fetch data from my backend. I received the (TypeError: Cannot read property 'map' of undefined) error multiple times and I've tried multiple fixes with no hope of fixing. I added a hook for loading to ensure that data is present before rendering, but If I go back and fourth between pages I receive the same error

Here's my code:

function Developer(props) {
    const [data, setData] = useState();
    const [loading , setLoading] = useState(true)
    const [error, setError] = useState();

    var id = props.match.params.id
    var location = props.match.params.location


    useEffect(() => {
        axios(`http://localhost:5000/api/${location}/${id}`)
        .then((response) => {
        setData(response.data);
        })
        .catch((error) => {
        console.error("Error fetching data: ", error);
        setError(error);
        })
        .finally(() => {
        setLoading(false);
        });
        }, []);
        
    if (loading) return "Loading...";
    if (error) return "Error!";

    return (
        <div>
            <h1>{data.Developer}</h1>
            <ul>
                {
                data.Projects.map((project) => (<li key = {project._id}>
                <a href= {`/${location}/${id}/${project._id}`}>{project.Project}</a></li> ))
                }
            </ul>
        </div> 
        )}
2
  • 1
    data.Projects is undefined. Can we see what the response from the api call looks like? Also you don't seem to be setting the loading variable to true before the api call. Because of that you may want to check that data isn't undefined in your jsx. Basically what looks to be happening is that you are trying to loop an undefined variable, but you haven't told the view to wait until the data has been fetched. The loading variable will do that for you setLoading(true) before the axios call. Then setLoading(false) before you setData. Commented Jun 2, 2021 at 21:18
  • data.Projects is a part of the collection being return by the axios call so it exists. I don't really get when to set my loading variable to true and when to false. could you please explain Commented Jun 2, 2021 at 22:10

4 Answers 4

1

you should do a check on the nature of your data property before making a map on it.

{
 data && data.Projects.length !== 0 &&
   data.Projects.map((project) => (<li key = {project._id}>
    <a href= {`/${location}/${id}/${project._id}`}>{project.Project}</a></li> ))
}

and i recommend you to use javascrip fetch

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

2 Comments

This would still give an error if data.Projects is not an array.
it because Projects is not an array. you can make a log of data.Projects to see the type of Projects. console.log(data.Projects);
0

I've encountered this very error myself many times.

First of all check if the data you are getting from the api is an array of javascript objects

Here is a simple solution.

{data ? data.Projects.map((project) => (<li key={project._id}>
                    <a href={`/${location}/${id}/${project._id}`}>{project.Project}</a></li>)) :<div>Loading..</div>}

So what I have done here is use a ternary operator, what it will do is check if data state has data yet, if it has it will show the data other wise it will show "Loading..."

Axios takes some time to fetch and set data, but the page renders before that so thats why it is showing Cannot read property 'map' of undefined

1 Comment

It's possible that data was defined, but not data.Projects, and your solution tells the users that it's still loading, while it's done and the data is missing. That would be misleading.
0

It's possible that the data returned doesn't contain what you need.

Try to print out the returned data

        axios(`http://localhost:5000/api/${location}/${id}`)
        .then((response) => {
          console.log(response.data);
          setData(response.data);
        })

it could be a valid response, but doesn't contain that variable data.Projects. If you want to handle the absence of that value, you can use something like:

(data.Projects || []).map(...)

this way, if data.Projects is falsy, it would be replaced by an empty array and it would call [].map(...).

Comments

0

as discussed, your setLoading(true) requirement.

function Developer(props) {
    const [data, setData] = useState();
    const [loading , setLoading] = useState(true)
    const [error, setError] = useState();

    var id = props.match.params.id
    var location = props.match.params.location


    useEffect(() => {
       // ooh look, we are in a loading state here, cause the data hasn't come back from the server at this point.
 setLoading(true);
        axios(`http://localhost:5000/api/${location}/${id}`)
           .then((response) => {
           setData(response.data);
           // awesome, data has returned from the server, we aren't loading anymore, but the finally block handles that scenario for us.

        })
        .catch((error) => {
          console.error("Error fetching data: ", error);
          setError(error);
        })
        .finally(() => {
          setLoading(false);
        });
        }, []);
        
    if (loading) return "Loading...";
    if (error) return "Error!";

    return (
        <div>
            <h1>{data.Developer}</h1>
            <ul>
                {
                data && data.Projects.length && data.Projects.map((project) => (<li key = {project._id}>
                <a href= {`/${location}/${id}/${project._id}`}>{project.Project}</a></li> ))
                }
            </ul>
        </div> 
        )}

Comments

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.