An Interest In:
Web News this Week
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
- April 19, 2024
- April 18, 2024
- April 17, 2024
How to manage global data with Context API, without Redux on React
Hi, Taishi here
How do you manage global data on React?
I used to use Redux for that, however, I currently use Context API for that purpose and I don't even install redux and redux-related packages!
2 ways to implement it with Context API
I think there are 2 ways to make it happen.
A simple and complicated one.
I am gonna explain the simple one first
Imagine we want to manage the logged-in user data.
1. Use state variable
First of all, we need a Context component for sure.
I found this way when I was reading next.js/userContext.js at master vercel/next.js
Add userContext.js
Let's make ./src/context/userContext.js
.
// File: ./src/context/userContext.jsimport React, { useState, useEffect, createContext, useContext } from 'react';import firebase from '../firebase/clientApp';export const UserContext = createContext();export default function UserContextComp({ children }) { const [user, setUser] = useState(null); const [loadingUser, setLoadingUser] = useState(true); // Helpful, to update the UI accordingly. useEffect(() => { // Listen authenticated user const unsubscriber = firebase.auth().onAuthStateChanged(async (user) => { try { if (user) { // User is signed in. const { uid, displayName, email, photoURL } = user; // You could also look for the user doc in your Firestore (if you have one): // const userDoc = await firebase.firestore().doc(`users/${uid}`).get() setUser({ uid, displayName, email, photoURL }); } else setUser(null); } catch (error) { // Most probably a connection error. Handle appropriately. } finally { setLoadingUser(false); } }); // Unsubscribe auth listener on unmount return () => unsubscriber(); }, []); return ( <UserContext.Provider value={{ user, setUser, loadingUser }}> {children} </UserContext.Provider> );}// Custom hook that shorhands the context!export const useUser = () => useContext(UserContext);
As you can see, UserContextComp
component has user
state variable.
const [user, setUser] = useState(null);
We store the user data in this user
variable and update it with setUser()
function.
Edit index.js
Now we have to use the UserContextComp
component to consume it!
Edit ./src/index.js
like below.
// File: ./src/index.jsimport React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import * as serviceWorker from './serviceWorker';import UserProvider from './context/userContext';ReactDOM.render( <React.StrictMode> <UserProvider> <App /> </UserProvider> </React.StrictMode>, document.getElementById('root'),);// If you want your app to work offline and load faster, you can change// unregister() to register() below. Note this comes with some pitfalls.// Learn more about service workers: https://bit.ly/CRA-PWAserviceWorker.unregister();
Now we can use user
variable and update it with setuser()
function everywhere
How to consume it
Import the useUser
function from the ./src/context/userContext.js
and get the variable you need.
In this case, we take loadingUser
, user
, and setUser
.
import React from 'react';import { useUser } from '../context/userContext';const MyComponent = () => { const { loadingUser, user, setUser } = useUser(); return ( <> {loadingUser ? ( <div>loading</div> ) : ( <div>Welcome, {user.displayName}</div> )} </> );};export default MyComponent;
Please just use setUser
if you need to update the user data just like when you update the usual state variable.
2. Use dispatch and reducer (more Redux way)
In this way, we use useContext with useReducer hook.
I feel like this way is Redux without Redux
Sure, Redux uses Context API inside itself.
By the way. I made an example app here.
Please take a look at this if you wanna make it happen on your local environment.
Anyway, let's dive into it!
Add ./src/context/reducer.js
If you are familiar with Redux, you can understand this with ease.
Now we are gonna define the reducer function and initialState.
The default value of user
is null
.
// File: ./src/context/reducer.jsexport const initialState = { user: null,};export const actionTypes = { SET_USER: 'SET_USER',};const reducer = (state, action) => { switch (action.type) { case actionTypes.SET_USER: return { ...state, user: action.user, }; default: return state; }};export default reducer;
Make ./src/context/StateProvider.js
// File: ./src/context/StateProvider.js`import React, { createContext, useContext, useReducer } from 'react';export const StateContext = createContext([]);export const StateProvider = ({ reducer, initialState, children }) => ( <StateContext.Provider value={useReducer(reducer, initialState)}> {children} </StateContext.Provider>);export const useStateValue = () => useContext(StateContext);
Set the Provider in ./src/index.js
Because of this, we can consume the StateContext component everywhere!
// File: ./src/index.jsimport React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import * as serviceWorker from './serviceWorker';+ import { StateProvider } from './context/StateProvider';+ import reducer, { initialState } from './context/reducer';ReactDOM.render( <React.StrictMode>+ <StateProvider initialState={initialState} reducer={reducer}> <App />+ </StateProvider> </React.StrictMode>, document.getElementById('root'),);// If you want your app to work offline and load faster, you can change// unregister() to register() below. Note this comes with some pitfalls.// Learn more about service workers: https://bit.ly/CRA-PWAserviceWorker.unregister();
Now display the logged in user's name!
Make a Auth component and use it in App.js
like below.
We need login/logout methods (handleLogin
, handleLogout
) to handle onclick events, so make them as well.
// File: ./src/App.jsimport React from 'react';import Auth from './Auth';import { auth, provider } from './firebase';import { useStateValue } from './context/StateProvider';import { actionTypes } from './context/reducer';import './App.css';function App() { const [state, dispatch] = useStateValue(); const handleLogin = async () => { try { const result = await auth.signInWithPopup(provider); dispatch({ type: actionTypes.SET_USER, user: result.user, }); } catch (err) { alert(err.message); } }; const handleLogout = async () => { await auth.signOut(); dispatch({ type: actionTypes.SET_USER, user: null, }); }; return ( <Auth> <div className="App"> <header className="App-header"> <div>{state.user?.displayName}</div> {state.user ? ( <button onClick={handleLogout}>Logout</button> ) : ( <button onClick={handleLogin}>Login</button> )} </header> </div> </Auth> );}export default App;
As the reference says, useReducer
returns state
and dispatch
.
An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. (If youre familiar with Redux, you already know how this works.)
That's why we can get variables like this.
useStateValue()
returns useContext(StateContext)
, and this returns useReducer(reducer, initialState)
.
const [state, dispatch] = useStateValue();
Now you see like this and you can login/logout.
If you logged in successfully, you can see your name as below.
When the value of state.user
is set, your name will be shown.
<div>{state.user?.displayName}</div>
Note
I think 2. Use dispatch and reducer (more Redux way)
may look complicated to you, but we can easily understand what kind of data this app manages globally in initialState. In this example, we manage only user
variable globally, but imagine if we manage like 10 variables
Hope this helps.
Original Link: https://dev.to/taishi/how-to-manage-global-data-with-context-api-not-redux-on-react-20m7
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To