Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Unexpected Behavior with React Context and JavaScript Promises

I’m working on a React v17 application where I’m using React’s Context API to manage global state. I have a context provider that fetches data from an API using JavaScript’s native fetch function, which returns a Promise.

Here’s a simplified version of my code:

import React, { createContext, useState, useEffect } from 'react';

export const MyContext = createContext();

export const MyProvider = props => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://myapi.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return (
    <MyContext.Provider value={{ data }}>
      {props.children}
    </MyContext.Provider>
  );
};

I’m using this context in a child component like below:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import React, { useContext } from 'react';
import { MyContext } from './MyProvider';

const MyComponent = () => {
  const { data } = useContext(MyContext);

  return (
    <div>
      {data ? data.map(item => <div key={item.id}>{item.name}</div>) : 'Loading...'}
    </div>
  );
};

The issue I’m facing is that sometimes, the child component renders before the data is fetched, causing it to throw an error because it’s trying to map over null. However, other times, it works perfectly fine.

I thought that useEffect with an empty dependency array would ensure that the fetch operation completes before anything else renders, but that doesn’t seem to be the case.

Can anyone explain why this might be happening and how to ensure that the fetch operation always completes before the child component tries to access the data?

Thank you in advance!

>Solution :

You’re dealing with fetch function’s asynchronous nature. It returns a Promise that completes when data is fetched from the API. JavaScript doesn’t pause for this Promise to complete, so your component might render before data fetch, causing an error when trying to map over null.

A solution: Add a loading state to your context provider.

import React, { createContext, useState, useEffect } from 'react';

export const MyContext = createContext();

export const MyProvider = props => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('https://myapi.com/data')
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, []);

  return (
    <MyContext.Provider value={{ data, loading }}>
      {props.children}
    </MyContext.Provider>
  );
};

In your child component, check if data is still loading before mapping over it:

import React, { useContext } from 'react';
import { MyContext } from './MyProvider';

const MyComponent = () => {
  const { data, loading } = useContext(MyContext);

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      {data.map(item => <div key={item.id}>{item.name}</div>)}
    </div>
  );
};

This way, your component shows "Loading…" initially and doesn’t map over data until it’s fetched from the API.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading