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:-
- 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:-
- Make sure to
encapsulate any component thet you wanna use the User context (this will be in 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>
);
}
- 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