skip to Main Content

I am trying to store the API response in the async/await functions into the response global variable which will be later accessed by App() but I am being prompted with the error shown below the code.

async function getAllTutors() {
    try{
        const response = await axios.get('API...');
        console.log("getAllTutors(): " + JSON.stringify(response.data.body));
        return response;
    }catch(error) {
        return [];
    }
    
}

var response = null;
(async () => {
    console.log("Retrieving Tutors");
    response = await getAllTutors();
    console.log("response (caller) = " +  JSON.stringify(response.data.body))
  })();

  
function App() {
...
    console.log("HERE: " + JSON.stringify(response.data.body));
    const [ data, setTutors ] = useState(employees);
...

error screenshot

I tried logging the values while in both functions and it does seem to output the desired JSON object but, App() still shows response variable to be undefined. I even tried to change App() to be async/await but I am being lead into other errors.

2

Answers


  1. Ok, so response is initially null.

    You’re calling on some functions that are async which are doing some fetching. At some point in time, those functions will finish fetching the data and update response to whatever the return from that API is. But JS doesn’t want to wait on anything it doesn’t need to unless you specifically tell it to. So when you get down to function App(), even though that code comes after all the logic above it, it’s being executed immediately after – before the response comes back and updates the ‘global’ variable response.

    Essentially, you’re calling response.data.body before response is set to the response from the API, due to the asynchronous nature of JS. Which errors because response is null.

    The pattern you’re implementing is wrong and weird. The response could be stored in state and the API call could be executed in a useEffect, eg:

    const [allTutors, setAllTutors] = useState(null);
    
    useEffect(() => {
       const fetchData = async () => {
           const response = await getAllTutors();
           setAllTutors(response.data.body);
       }
       fetchData();
    }, []);
    

    Or something similar to that structure, with maybe some things changed based on what the API is returning.

    Login or Signup to reply.
  2. The simplest way to achieve this is to store a promise that resolves with the value. Then you can re-use that promise anywhere.

    The issue with this approach in React is that it is not reactive; you won’t trigger any re-renders when data changes.

    I would recommend using a context instead to wrap any components requiring the data (or wrap your entire app).

    For example

    import { createContext, useContext, useEffect, useState } from "react";
    
    const TutorsContext = createContext({ tutors: [] }); // default value
    
    const getAllTutors = async () => (await axios.get("API...")).data.body;
    
    // context provider
    const TutorsProvider = ({ children }) => {
      const [tutors, setTutors] = useState([]);
    
      useEffect(() => {
        getAllTutors().then(setTutors).catch(console.error);
      }, []);
    
      return (
        <TutorsContext.Provider value={{ tutors }}>
          {children}
        </TutorsContext.Provider>
      );
    };
    
    // a handy hook
    const useTutors = () => useContext(TutorsContext);
    
    export { TutorsProvider, useTutors };
    

    You can then wrap any components in the provider, eg

    <TutorsProvider>
      <App />
    </TutorsProvider>
    

    and in those components…

    const { tutors } = useTutors();
    

    This will ensure the data is only retrieved once when the context is initialised. It can then be shared with any components you want.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search