Search code examples
reactjsreact-context

Why add empty curly braces in createContext() to help handle API data


Say I have the following in my Context component:

export const AppContext = createContext({});

export const Provider = ({children}) => {

    const [data, setData] = useState([]);

    const getData = async () => {
        try {
            await axios.get(<apiUrl>)
                       .then(data => setData(data.data));      
        } catch (err) {
            console.error(err.message);
        }
    }
    
    useEffect(() => {
        getData();
    }, []);

// etc. code below

I then call useContext(appContext) in another component to retrieve the prop carrying the data from this provider.

This prop holds API data which is an array of objects. In a certain circumstance, I would want to grab only the data from the first object of this array by writing prop[0]. Now maybe I want to grab the value from a certain property of this object by then writing prop[0].objProp

My question: Why does this only work when there are empty curly braces inside the createContext() call at the beginning of the example? Because without that, I would receive the error "Property 'objProp' of undefined".


Solution

  • Explanation

    You are not showing all relevant code to determine what exactly is going wrong, but my guess is that you have not provided a value for the value prop when rendering AppContext.Provider.

    According to the documentation (https://reactjs.org/docs/context.html#reactcreatecontext):

    The defaultValue argument is only used when a component does not have a matching Provider above it in the tree.

    So if a matching provider is not found the default value of of createContext is used. If you don't pass anything this will be undefined, if you pass {} it will be an object.

    Let's say you have the following code:

    const c = React.useContext(AppContext)
    console.log(c.value)
    

    When createContext is not passed a default argument the previous code will error, because you try to access a property on undefined. If the default value was set, the value of c.value will just be undefined, because there is no property named something on an empty object.


    Working Example

    Now what I actually think you should do is to provide the value prop on AppContext.Provider and do something like this:

    import React from "react";
    import axios from "axios";
    
    const AppContext = React.createContext();
    
    const Provider = ({ children }) => {
      const [data, setData] = React.useState([{title: ''}]); // Need default value with all fields in the object you plan on receiving
    
      const getData = async () => {
        try {
          await axios
            .get("https://jsonplaceholder.typicode.com/posts")
            .then((data) => {
              setData(data.data);
            });
        } catch (err) {
          console.error(err.message);
        }
      };
    
      React.useEffect(() => {
        getData();
      }, []);
    
      return (
        <AppContext.Provider value={{data: data}}>
          {children}
        </AppContext.Provider>
      );
    };
    
    const TestComponent = () => {
      const c = React.useContext(AppContext) 
      return c.data[0].title // Do something with the data...
    };
    
    const App = () => {
      return (
        <div>
          <Provider>
            <TestComponent />
          </Provider>
        </div>
      );
    };
    
    export default App;