Fahad Nadeem
Fahad Nadeem

Reputation: 67

How can I change state of another reducer from extra reducers add cases

I am trying to create a notification Component. My notification Component is at root level and when ever user tries to login the process of the the async function is relayed to him i.e. pending fulfilled or rejected.

The Problem is that I don't know how to call notification reducer from userSlice or even if its possible is this a good way or not.

User Slice

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";

const initialUserState = { 
    currentUser:null 
}

export const getUser = createAsyncThunk(
    'user/getUser',
    async (endpoint, data) => {
        return(
            await axios.post(endpoint, data)
            .then(res =>{
                return res.data.user
            })
            .catch(error =>{
                throw Error(error.response.data)
            })
        )
    }
)

const userSlice = createSlice({
    name: 'user',
    initialState: initialUserState,
    reducers:{
        currentUser(state, action){
            state.currentUser = action.payload
        }
    },
    extraReducers: 
        (builder) => {
            builder.addCase(getUser.pending, ()=>{
                console.log("authing")
            })
            builder.addCase(getUser.fulfilled, (state, action)=>{
                state.currentUser = action.payload
                console.log("fulfilled")
            })
            builder.addCase(getUser.rejected, (state, action)=>{
                console.log("failed")
                alert(action.error.message)
            })
        }
})

export const userActions = userSlice.actions;
export default userSlice.reducer;

notificationSlice

import React from 'react'
import { useSelector } from 'react-redux'

function Notification() {

    const toast = useSelector(state => state.notification)
    console.log(toast)
    return (
        toast.active &&
        <div className="notification" style={{backgroundColor:toast.backgroundColor}} >
            {toast.message}
        </div>
    )
}

export default Notification

I want to change notification state when ever one of the extra reducer in userSlice is called

Upvotes: 0

Views: 3302

Answers (1)

BryanOfEarth
BryanOfEarth

Reputation: 725

I think you are thinking about this almost exactly backwards. What you want is NOT to "call notification reducer from userSlice," but to LISTEN for userSlice actions in a notificationSlice.

I have done something like the following, which I think would work well for you:

import { createEntityAdapter, createSlice, isAnyOf } from '@reduxjs/toolkit'

const notificationsAdapter = createEntityAdapter()

const initialState = notificationsAdapter.getInitialState({
  error: null,
  success: null,
})

const notificationsSlice = createSlice({
  name: 'notifications',
  initialState,
  reducers: {
    clearNotifications: state => {
      state.error = null
      state.success = null
    },
    setError: (state, action) => {
      state.success = null
      state.error = action.payload
    },
    setSuccess: (state, action) => {
      state.success = action.payload
      state.error = null
    },
  },
  extraReducers: builder => {
    builder
      .addMatcher(
        isAnyOf(
          getUser.fulfilled,
        ),
        (state, action) => {
          state.error = null
          state.success = action.payload.message
        }
      )
      .addMatcher(
        isAnyOf(
          getUser.rejected
          // can add as many imported actions
          // as you like to these
        ),
        (state, action) => {
          state.error = action?.payload
          state.success = null
        }
      )
      // reset all messages on pending
      .addMatcher(
        isAnyOf(
          getUser.pending
        ),
        (state, action) => {
          state.error = null
          state.success = null
        }
      )
  },
})
export const { clearNotifications, setError, setSuccess } = notificationsSlice.actions

export default notificationsSlice.reducer

export const getErrorMsg = state => state.notifications.error
export const getSuccessMsg = state => state.notifications.success

Having added the above, you can now create a notification component that listens for

  const error = useSelector(getErrorMsg)
  const success = useSelector(getSuccessMsg)

and shows the messages accordingly.

Caveat:

  • My notificationSlice code assumes that when an action completes, there will exist a "message" object on the success payload. So, on my async thunks, if my api does not return this I must add this explicitly to the result.

Upvotes: 1

Related Questions