Do actions added with extraReducers on createSlice have the slice's name prefix added to their types? - redux

From the official doc's example:
https://redux-toolkit.js.org/api/createSlice#the-extrareducers-builder-callback-notation
import { createAction, createSlice } from '#reduxjs/toolkit'
const incrementBy = createAction<number>('incrementBy')
const decrement = createAction('decrement')
createSlice({
name: 'counter',
initialState: 0,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(incrementBy, (state, action) => { // DO SOMETHING })
.addCase(decrement, (state, action) => { // DO SOMETHING })
.addDefaultCase((state, action) => {})
},
})
Also from the docs:
One of the key concepts of Redux is that each slice reducer "owns" its slice of state, and that many slice reducers can independently respond to the same action type. extraReducers allows createSlice to respond to other action types besides the types it has generated.
QUESTION
In the example above, will the cases incrementBy and decrement also get the counter name as a prefix in their types?
Like:
"counter/incrementBy"
"counter/decrement"
Is this how the extraReducers property work?

No, because the entire point of extraReducers is that it does not generate any new action types.
extraReducers exists so that a slice reducer can listen to other action types that have already been defined outside the slice.

No. It does not get the name prefix.
https://codesandbox.io/s/xenodochial-dew-35ivq
import { createAction, createSlice } from "#reduxjs/toolkit";
interface CounterState {
value: number;
}
export const decrementV2 = createAction('decrement');
const initialState = { value: 0 } as CounterState;
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment(state,action) {
console.log(`action.type: ${action.type}`);
state.value++;
},
decrement(state,action) {
console.log(`action.type: ${action.type}`);
state.value--;
}
},
extraReducers: (builder) => {
builder.addCase(decrementV2, (state, action) => {
console.log("FROM decrementV2 (from extraReducers)")
console.log(`action.type: ${action.type}`);
state.value--;
});
}
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

Related

How to add cases in ExtraReducer to match the actions created in currentReducer using createSlice() from #reduxjs/toolkit

Here below I have mentioned a redux slice. A fetchAllApps thunk function is created with createAsyncThunk for action 'allApps/allappsAdded/' which I dynamically got by allAppsAdded.type. When the fetchAllapps is dispatched it generated actions of type 'allApps/allappsAdded/pending', 'allApps/allappsAdded/fulfilled', 'allApps/allappsAdded/rejected' which I need to add in extraReducers to handle it by doing hardcode.Is there any way to add these action types like allAppsAdded.type programatically?. so that in future It makes easy for me to change these without redundant..
import {
configureStore,
createAsyncThunk,
createSlice
} from "#reduxjs/toolkit";
const initialState = {
apps: [],
categories: [],
loading: {
apps: false
}
};
const allappsSlice = createSlice({
name: "allapps",
initialState,
reducers: {
allappsAdded: (state, action) => {
state["apps"] = action.payload.apps;
state["categories"] = action.payload.categories;
}
},
extraReducers: {
}
});
export default () =>
configureStore({
reducer: allappsSlice.reducer
});
const { allappsAdded } = allappsSlice.actions;
const fetchAllApps = createAsyncThunk(allappsAdded.type, async () => {
console.log("ss");
setTimeout(() => ({ apps: [], categories: [] }), 2000);
});
export { allappsAdded, fetchAllApps };

how can i export a reducer object in redux

am new to redux, i created a addToBasket and removeBasket object in my reducers variable but when am trying to export it so i can use it in another component, am getting an Error
TypeError: Cannot destructure property 'addToBasket' of 'basketSlice.action' as it is undefined. i don't know if am not destructuring it the right way, please can someone help out, i don't know what am doing wrong
here is my code
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
items: [],
}
export const basketSlice = createSlice({
name: "basket",
initialState,
reducers: {
addToBasket: (state, action) => {
state.items = [...state.items, action.payload]
},
removeFromBasket: (state, action) => {},
}
});
export const { addToBasket, removeFromBasket} = basketSlice.action;
export default basketSlice.reducer;
You have a typo there - it's actions, not action.
export const { addToBasket, removeFromBasket} = basketSlice.actions;

How to set two separate States with Redux Toolkit?

I'm trying to establish two separate states with Redux Toolkit, one called posts and another called countTest. However, at the moment the two states share the same value.
posts is set to display a value of [] and countTest is set to display a value of 0. How do I differentiate the two states to display their unique value?
My actions file
import { createSlice } from "#reduxjs/toolkit";
import { database, auth } from "../firebase";
import { ref, set } from "firebase/database";
export const counterSlice = createSlice({
name: "posts",
initialState: {
value: [],
},
reducers: {
createAccount: (state, action) => {
const user = auth.currentUser;
const uid = user.uid;
set(ref(database, `users/${uid}`), {
email: action.payload.email,
name: action.payload.name,
});
},
},
});
export const testSlice = createSlice({
name: "countTest",
initialState: { value: 0 },
reducers: {
incrementAmount: (state, action) => {
state.value = state.value + 1;
},
decrementAmount: (state, action) => {
state.value = state.value - 1;
},
},
});
export const { createAccount, countTest } = counterSlice.actions;
export default counterSlice.reducer;
My store file
import { configureStore } from "#reduxjs/toolkit";
import counterReducer from "./actions";
export const store = configureStore({
reducer: {
posts: counterReducer,
countTest: counterReducer,
},
});
I know in my store file I'm using counterReducer without specifically referring to the actions createAccount and countTest. How do I go about retrieving the unique values of each and displaying in store? Do I need to create a separate file for each action (is this best practice?) instead of having all the actions in one file?
Thank you for any help
You need to use a createAction (on an another file or on one of your slice import action from it) and use extraReducer.
Doc :
https://redux-toolkit.js.org/api/createSlice (the last exemple)
exemple create action:
export const incrementAll = createAction("incrementAll");
use on your slices :
reducers : {//what you have for other things},
extraReducers: (builder) => {
builder.addCase(incrementAll, (state) => {
state.value++;
});
See the doc for more information or comment here if you need more explanation

extraReducers in createSlice() in Redux Toolkit

Here's the example code from Codecademy:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import { client } from '../api';
const initialState = {
todos: [],
status: 'idle'
};
export const fetchTodos = createAsyncThunk('todos/fetchTodos', async () => {
const response = await client.get('/todosApi/todos');
return response.todos;
});
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
addTodo: (state, action) => {
state.todos.push(action.payload);
}
},
extraReducers: {
[fetchTodos.pending]: (state, action) => {
state.status = 'loading';
},
[fetchTodos.fulfilled]: (state, action) => {
state.status = 'succeeded';
state.todos = state.todos.concat(action.payload);
}
}
});
I'm confused about what fetchTodos.pending and fetchTodos.fulfilled mean as computed properties. I don't see that fetchTodos has those attributes. What is going on?
Those are generated by createAsyncThunk
Check out the RDK docs on createAsyncThunk
Parameters#
createAsyncThunk accepts three parameters: a string action type value, a payloadCreator callback, and an options object.
type
A string that will be used to generate additional Redux action type
constants, representing the lifecycle of an async request:
For example, a type argument of 'users/requestStatus' will generate these
action types:
pending: 'users/requestStatus/pending'
fulfilled: 'users/requestStatus/fulfilled'
rejected: 'users/requestStatus/rejected'

redux-toolkit sharing state between slice reducer

I'm building an app where a "slice reducer" needs to access state of another "slice reducer". The redux docs talks about using a custom combine reducer in order to pass in the root state to the reducer - Beyond combineReducers
Thus far, I have this for my root reducer:
import cats from '../slices/cats'
import dogs from '../slices/dogs'
import status from '../slices/status'
function combinedReducer(state = {}, action) {
return {
status: status(state.status, action),
dogs: dogs(state.dogs, action),
cats: cats(state.cats, action, state),
};
}
export default configureStore({ reducer: combinedReducer });
I don't seem to be able to get the root state for my cats reducer - passed in as the 3rd arg above.
const assetsSlice = createSlice({
name: 'cats',
initialState,
reducers: {
setFetched: (state, { payload }, root) => {
// root is undefined
state.type = payload + root.dogs.legs;
},
},
});
This should work, no?
If I use a vanilla reducer that's not created by createSlice I am able to get the root state
export default (state = initialState, action, root) => {
// root - { status: {}, dogs: {}, cats: {} }
};
This is not possible as a third argument since RTK's reducers only pass the first two arguments to the case reducers.
You could just add it to the action though (but granted, that's hacky):
function combinedReducer(state = {}, action) {
const actionWithFullState = { ...action, meta: {...action.meta, fullState: state }}
return {
status: status(state.status, action),
dogs: dogs(state.dogs, action),
cats: cats(state.cats, actionWithFullState),
};
}

Resources