Tutorial
Redux Setup

Redux Setup

Redux Setup

State folder -> index.js

import { createSlice } from "@reduxjs/toolkit";
 
// Define the initial state of the application
const initialState = {
  mode: "light", // Represents the current theme mode ("light" or "dark")
  user: null, // Represents the currently logged-in user
  token: null, // Represents the authentication token
  posts: [], // Represents a list of posts
};
 
// Create a slice of the Redux store using the createSlice function from Redux Toolkit
export const authSlice = createSlice({
  name: "auth", // Name of the slice
  initialState, // Initial state of the slice
  reducers: {
    setMode: (state) => {
      // Toggle the theme mode between "light" and "dark"
      state.mode = state.mode === "light" ? "dark" : "light";
      // Here, `state.mode` represents the previous theme mode (mode: "light"),
      // and `state.mode === "light" ? "dark" : "light"` represents the new theme mode.
    },
    setLogin: (state, action) => {
      // Set the logged-in user and authentication token
      state.user = action.payload.user;
      state.token = action.payload.token;
      // Here, `action.payload` represents the data passed when dispatching this action.
      // `action.payload.user` represents the user data,
      // and `action.payload.token` represents the authentication token.
    },
    setLogout: (state) => {
      // Clear the logged-in user and authentication token
      state.user = null;
      state.token = null;
    },
    setFriends: (state, action) => {
      if (state.user) {
        // Set the friends of the logged-in user
        state.user.friends = action.payload.friends;
        // Here, `action.payload.friends` represents the list of friends passed in the action payload.
      } else {
        console.error("user friends non-existent :(");
        // If there is no logged-in user, an error message is logged to the console.
      }
    },
    setPosts: (state, action) => {
      // Set the list of posts
      state.posts = action.payload.posts;
      // Here, `action.payload.posts` represents the list of posts passed in the action payload.
    },
    setPost: (state, action) => {
      // Update a specific post in the list of posts
      const updatedPosts = state.posts.map((post) => {
        if (post._id === action.payload.post._id) {
          // If the post's _id matches the updated post's _id,
          // replace it with the updated post in the new array
          return action.payload.post;
        }
        return post;
      });
      state.posts = updatedPosts;
      // Here, `action.payload.post` represents the updated post passed in the action payload.
    },
  },
});
 
// Extract the actions generated by the createSlice function
export const {
  setMode,
  setLogin,
  setLogout,
  setFriends,
  setPosts,
  setPost,
} = authSlice.actions;
 
export default authSlice.reducer;

We are using index.ts so we should modify this and using interfaces in TypeScript is a good practice for defining the types of your state and payload objects.

  1. Added the User interface: Represents the structure of a user object with properties id, name, and friends.

  2. Added the Post interface: Represents the structure of a post object with properties _id and any other relevant post properties.

  3. Added the AuthState interface: Represents the structure of the authentication state object, including properties mode, user, token, and posts.

  4. Updated the initialState declaration to use the AuthState interface.

  5. Updated the payload types in the action creators:

    • setLogin action now accepts an object with properties user of type User and token of type string.
    • setFriends action now accepts an object with a friends property of type string[].
    • setPosts action now accepts an object with a posts property of type Post[].
    • setPost action now accepts an object with a post property of type Post.
  6. Modified the setFriends reducer to use the produce function from the immer library to handle the immutability of the nested user object. The produce function ensures that the state is not modified directly and allows you to make

import { createSlice, Draft, PayloadAction } from "@reduxjs/toolkit";
import produce from "immer";
 
interface User {
  id: string;
  name: string;
  friends: string[];
}
 
interface Post {
  _id: string;
  // Other post properties
}
 
interface AuthState {
  mode: string;
  user: User | null;
  token: string | null;
  posts: Post[];
}
 
const initialState: AuthState = {
  mode: "light",
  user: null,
  token: null,
  posts: [],
};
 
export const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    setMode: (state) => {
      state.mode = state.mode === "light" ? "dark" : "light";
    },
    setLogin: (
      state,
      action: PayloadAction<{ user: User; token: string }>
    ) => {
      state.user = action.payload.user;
      state.token = action.payload.token;
    },
    setLogout: (state) => {
      state.user = null;
      state.token = null;
    },
    setFriends: (state, action: PayloadAction<{ friends: string[] }>) => {
      state.user = produce(state.user, (draft: Draft<User | null>) => {
        if (draft) {
          draft.friends = action.payload.friends;
        } else {
          console.error("User object is null");
        }
      });
    },
    setPosts: (state, action: PayloadAction<{ posts: Post[] }>) => {
      state.posts = action.payload.posts;
    },
    setPost: (state, action: PayloadAction<{ post: Post }>) => {
      state.posts = state.posts.map((post) =>
        post._id === action.payload.post._id ? action.payload.post : post
      );
    },
  },
});
 
export const {
  setMode,
  setLogin,
  setLogout,
  setFriends,
  setPosts,
  setPost,
} = authSlice.actions;
 
export default authSlice.reducer;