State Management using react hooks in Next.js
First we nee create a store then export it with useContext hook
1
2
3
4
5
|
import { createContext, useContext } from "react";
const Store = createContext();
export const useStore = () => useContext(Store);
|
Then we need create a reducer function takes state and action as an arguments, we will
change the state base on action type with switch
1
2
3
4
5
6
7
8
9
|
const reducer = (state, action) => {
console.log(action);
switch (action.type) {
default: {
return state;
}
}
};
|
A store provider needs to be created and the reducer with actions and states need to be assign to it; action will be dispatched.
1
2
3
4
5
|
export const StoreProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, {});
return <Store.Provider value={[state, dispatch]}>{children}</Store.Provider>;
};
|
we can provide dummy data via useReducer hook.
1
|
const [state, dispatch] = useReducer(reducer, { name: "donald duck" });
|
if we wrap with in _app.js file, we can access state in anywhere in the app.
we can call usStore within header.jsx and reach to state.
1
2
|
const [state, dispatch] = useStore();
console.log({ state });
|
Constants
1
2
3
4
5
|
export const authConstants = {
LOGIN_REQUEST: "LOGIN_REQUEST",
LOGIN_SUCCESS: "LOGIN_SUCCESS",
LOGIN_FAILURE: "LOGIN_FAILURE",
};
|
now we can place real use authentication data in useReducer instead of name:{}
1
2
3
4
5
6
|
user:{
authenticated:false,
authenticating:false,
error:null
}
|
we can dispatch type of the action and initiate state change in login.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
|
const payload = { email, password };
dispatch({ type: authConstants.LOGIN_REQUEST });
const res = await signIn("credentials", { ...payload, redirect: false }); //next-auth
console.log({ res });
if (!res.error) {
const session = await getSession();
dispatch({ type: authConstants.LOGIN_SUCCESS, payload: session });
router.replace("/");
} else {
dispatch({ type: authConstants.LOGIN_FAILURE, payload: res.error });
setErrorMessage(res.error);
}
```
|
if we define all the action types in context/index.js , we can dispatch them and our state management will be completed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
switch (action.type) {
case authConstants.LOGIN_REQUEST: {
return { ...state, user: { authenticating: true, ...state.user } };
}
case authConstants.LOGIN_SUCCESS: {
return {
...state,
user: {
authenticating: false,
authenticated: true,
...action.payload.user,
},
};
}
case authConstants.LOGIN_FAILURE: {
return {
...state,
user: {
...state.user,
error: action.payload,
},
};
}
default: {
return state;
}
}
|
Finally we can fetch state in the client side end update interface accordingly
1
2
3
4
5
6
7
|
const [state, dispatch] = useStore();
const user = getValue(state, ["user"]); // state.user
const authenticated = getValue(state, ["user", "authenticated"], false);
// state.user.authenticated
{authenticated ? (
<button>log out</button>
) : ( <button>log in</button> }
|
state management in sign up pages
1
2
3
|
const [state] = useStore();
const user = getValue(state, ["user"], null);
// look at state.user, if it empty assign null
|