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