简体   繁体   中英

Call reducer from a different slice Redux Toolkit

I've got an authSlice

const authSlice = createSlice({
  name: 'authStore',
  initialState,
  reducers: {
    logout(state = initialState) {
      return { ...state, isAuthenticated: false };
    },
  },
  extraReducers: (builder) => {
    builder.addCase(login.fulfilled, (state, { payload }) => {
      state.isAuthenticated = true;
      localStorage.setItem('userId', payload.userId);
    });

    builder.addCase(login.pending, (state) => {
      state.isLoading = true;
    });

    builder.addCase(login.rejected, (state, { payload, error }) => {
      if (payload) {
        state.loginError = payload;
        state.isLoading = false;
      } else {
        state.loginError = error;
      }
    });
  },
});

and a userSlice :

const userSlice = createSlice({
  name: 'userStore',
  initialState,

  reducers: {
    clearUser(state = initialState) {
      return { ...state };
    },
  },

  extraReducers: (builder) => {
    builder.addCase(getUser.fulfilled, (state, { payload }) => {
      state.user = payload;
      state.isLoading = false;
    });

    builder.addCase(getUser.pending, (state) => {
      state.isLoading = true;
    });

    builder.addCase(getUser.rejected, (state) => {
      state.isLoading = false;
    });
  },
});

I have a few questions about my code:

  1. How can I call clearUser() in userSlice from authSlice ?
  2. Is this good practice or an anti-pattern?
  3. If it's an anti-pattern what is an alternative approach to doing this whilst keeping the separation of the authSlice and the userSlice ?

You wouldn't want to dispatch the clearUser action from the authSlice.

You could call it from an asyncThunk (or normal thunk) instead, but definitely not from a reducer.

Another possibility is that you make the userSlice have an extra reducer case for the logout action

// This is assuming that you are exporting the actions from the authSlice
builder.addCase(authActions.logout,(state=initialState)=>({...state}))

Which is based on your clearUser reducer (which wouldn't actually change the state)

If you wanted to reset the state to the initial state, it would be:

builder.addCase(authActions.logout,(state)=>initialState)

I ended up using a combination of Zachary's answer and referencing a SO post here .

To have a logout function that clears all state with one call, I needed to create a rootReducer which contained logic to clear the state:

import { AnyAction, combineReducers, Reducer } from '@reduxjs/toolkit';
import auth from './auth/authSlice';
import user from './user/userSlice';

const combinedReducer = combineReducers({
  auth,
  user,
});

export const rootReducer: Reducer = (state: RootState, action: AnyAction) => {
  if (action.type === 'authStore/logout') {
    localStorage.clear();
    state = {} as RootState;
  }
  return combinedReducer(state, action);
};

export type RootState = ReturnType<typeof combinedReducer>;

In the authSlice I then created an empty logout() reducer:

const authSlice = createSlice({
  name: 'authStore',
  initialState,
  reducers: {
    logout: (state) => {
      /* declared here, handled in the root reducer: ../rootReducer.ts */
    },
  }
})

This was finally consumed inside my component, like so:

import React from 'react';
import { useDispatch } from 'react-redux';
import { logout } from '../store/auth/authSlice';

export const Home = () => {
  const dispatch = useDispatch();
  return <button onClick={() => dispatch(logout())}>Logout</button>;
};

In the case that I needed to do anything async in the logout process, I would have to use a method similar to what Zachary described (with a Thunk) inside my rootReducer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM