Search code examples
javascriptreactjsnext.jsreact-reduxredux-toolkit

How to trigger an action inside another action? Next Redux


I want to implement the logic to authorize the user when the page loads. Initially, I wanted to check if the token was in the cookies (checkUserToken) and if it was or was not - call another function (fetchUserData), which in the future would make a request to the server. Finally, when the server responded - call a third function (setUserData), which would fill userData with user data

'use client'

import { createSlice } from "@reduxjs/toolkit";
import { getCookie } from '@/app/untils/Cookies';

const initialState = {
  userData: null
}

export const userSlice = createSlice({
  name: "userData",
  initialState,
  reducers: {
    checkUserToken: ()  => {
      console.log('chekc')
      const token = getCookie('user-token');
      console.log(token)
      if (token)
        return fetchUserData(token)
      else
        return fetchUserData(false)
    },
    fetchUserData: async (state, action) => dispatch => {
      return  console.log('FETCH')
      // console.log(state)
      // console.log(action)
    },
    setUserData: (state, action) => {
      console.log('SET USER')
      console.log(action)
      console.log(state)
    }
  }
})

export const { checkUserToken, fetchUserData, setUserData } = userSlice.actions

export default userSlice.reducer

How can I implement something like this in my slice?


Solution

  • Reducer functions are pure functions, they don't do side-effects like dispatching actions. checkUserToken can't dispatch any actions, and fetchUserData can't return a function value. It appears to me that checkUserToken and fetchUserData are actually asynchronous actions. Create a thunk action for these.

    Example:

    import { createSlice, createAsyncAction } from "@reduxjs/toolkit";
    import { getCookie } from '@/app/untils/Cookies';
    
    export const checkUserToken = createAsyncAction(
      "userData/checkUserToken",
      (_, thunkAPI) => {
        console.log('check');
        const token = getCookie('user-token');
        console.log(token);
        
        return thunkAPI.dispatch(fetchUserData(token ?? false));
      },
    );
    
    export const fetchUserData = createAsyncAction(
      "userData/fetchUserData",
      async (token, thunkAPI) => {
        try {
          console.log('FETCH');
          ... asynchronous fetching logic ...
          return /* some data??? */
        } catch(error) {
          return thunkAPI.rejectWithValue(error));
        }
      },
    );
    
    const initialState = {
      userData: null,
    };
    
    export const userSlice = createSlice({
      name: "userData",
      initialState,
      reducers: {
        setUserData: (state, action) => {
          console.log('SET USER');
          ....
        }
      },
      extraReducers: builder => {
        builder
          .addCase(fetchUserData.fulfilled, (state, action) => {
            // update the user data with returned action.payload value
          })
          .addCase(fetchUserData.rejected, (state, action) => {
            // set any error state, if necessary
          });
      },
    })
    
    export const { setUserData } = userSlice.actions;
    
    export default userSlice.reducer;
    

    Note that it would be trivial for the fetchUserData action to access the cookies/token directly, and that the fetchUserData.fulfilled reducer case can set/update the user data state as well. This means that setUserData and checkUserToken actions are likely unnecessary.