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