How can I use an axios get call inside a Context provider in react

Viewed 8056

I want to make a call to get the user information and use it in different components throughout the application, so I am using react.Context and I managed to move a string around between components but when I tried to use the axios.get I was not able to get the data to another component, here is the context code:

import React, { createContext } from 'react';
import axios from 'axios';

export const userContext = createContext();

export const UserProvider = (props) => {
    const config = {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `JWT ${localStorage.getItem('access')}`,
            'Accept': 'application/json'
        }
    };
    const res = axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config)
    return(
    <userContext.Provider value= { res }>{props.children}</userContext.Provider>
    );
} 

can someone please tell me what I did wrong here

1 Answers

It would be more manageable if you could handle the state in hassle free way. Instead of passing the res which I reckoned going to be your state, directly to the Provider. Which I think, in time, if you have more than one state you want to add to your User context. You gonna need to pass them too as value. That would not be flexible.

I would suggest you to manage them like so:-

  1. Having dedicated User context folder: src/contexts/User
  • src/contexts/User/UserContext (creating User context)
import { createContext } from "react";

export const UserContext = createContext();

  • src/contexts/User/UserReducer (handle state update)
export default (state, action) => {
  switch (action.type) {
    case "SET_USER":
      return {
        ...state,
        user: action.payload
      };
    case "SET_ERROR":
      return {
        ...state,
        error: action.payload.error,
        message: action.payload.message
      };
    case "SET_LOADING":
      return {
        ...state,
        loading: action.payload
      };
    default:
      return state;
  }
};

  • src/contexts/User/UserAction (handle action done to state. Basically anything that you need to make the changes to your state)
import axios from "axios";

// Set Loading
export const setLoading = (dispatch, status) =>
  dispatch({ type: "SET_LOADING", payload: status });

// Set Error
export const setError = (dispatch, error) =>
  dispatch({
    type: "SET_ERROR",
    payload: { error: error.status, message: error.message }
  });

// Set User (get user info)
export const getUser = async dispatch => {
  setLoading(dispatch, true);

  // do fetch
  await axios
    .get(`https://jsonplaceholder.typicode.com/users/1`)
    .then(res => {
      const result = res.data;

      // set user info
      dispatch({
        type: "SET_USER",
        payload: result
      });
    })
    .catch(error => {
      const result = error;

      // set error if has any
      dispatch({
        type: "SET_ERROR",
        payload: {
          error: true,
          message: result
        }
      });
    });
};

  • src/contexts/User/UserState (handle state initialization. You can later add more or initialize more state in const initialState below if you have any. Plus you don't need to add more value in future tho.)
import React, { useContext, useReducer } from "react";
import { UserContext } from "./UserContext";
import UserReducer from "./UserReducer";

export const useUser = () => {
  const { state, dispatch } = useContext(UserContext);
  return [state, dispatch];
};

export const UserState = ({ children }) => {
  const initialState = {
    user: {},
    loading: false,
    error: false,
    message: ""
  };

  const [state, dispatch] = useReducer(UserReducer, initialState);

  return (
    <UserContext.Provider
      value={{
        state: state,
        dispatch: dispatch
      }}
    >
      {children}
    </UserContext.Provider>
  );
};

Then you can use them like so:-

  1. Make sure to encapsulate any component thet you wanna use the User context (this will be in App.js)
  • App.js:-
import React from "react";
import { UserState } from "./contexts/User/UserState";
import Demo from "./Demo";
import "./style.css";

export default function App() {
  return (
    <UserState>
      <Demo />
    </UserState>
  );
}

  1. You can use any state initialized in UserState by using useState:-
  • Demo.js (App.js child component):-
import React, { useEffect } from "react";
import { useUser } from "./contexts/User/UserState";
import { getUser, setLoading } from "./contexts/User/UserAction";
import "./style.css";

const Demo = () => { 
  const [userState, userDispatch] = useUser();
  const { user, loading, error, message } = userState;

  // get user info handler
  const getUserInfoHandler = async () => {
    await getUser(userDispatch);
    setLoading(userDispatch, false);
  };

  // get user info
  useEffect(() => {
    getUserInfoHandler();
  }, []);

  return (
    <div>
      {loading && <p>Loading...</p>}
      {error && <p>{message}</p>}
      <p>User name: {user.name}</p>
    </div>
  );
};

export default Demo;

You may see the working code here in sandbox

Related