I am reading the Redux official tutorial. I surprised when I see the counterReducer which there is not in any modules bu has value and works! I couldn't find any description of it but :
Since we know that the counterReducer function is coming from
features/counter/counterSlice.js, let's see what's in that file, piece
by piece.
I even change the name of the slicer to the counter3 but it still works!
Can anyone describe to me please how it is generated?
counterSlice.js
import { createSlice } from '#reduxjs/toolkit';
export const counterSlice = createSlice({
name: 'counter3',
initialState: {
value: 0,
},
reducers: {
increment: state => {
state.value += 1;
},
decrement: state => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export const incrementAsync = amount => dispatch => {
setTimeout(() => {
dispatch(incrementByAmount(amount));
}, 1000);
};
export const selectCount = state => state.counter.value;
export default counterSlice.reducer;
store.js (We use it in store.js module)
import { configureStore } from '#reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice'; //HERE IS THE IMPORTING PLACE
export default configureStore({
reducer: {
counter: counterReducer,
},
});
https://redux.js.org/tutorials/essentials/part-2-app-structure#creating-slice-reducers-and-actions
The counterSlice.js is using an ES6 "default export" to export the reducer function generated by createSlice:
export default counterSlice.reducer;
Any other file can then use a "default import" to get a reference to that reducer function. However, when we default import a value, we can give it any name we want:
// fileA.js
import counterReducer from "./features/counter/counterSlice"
// fileB.js
import someReducer from "./features/counter/counterSlice"
// fileC.js
import fred from "./features/counter/counterSlice"
All three of those variables will point to the same reducer function - it's just a question of what the local variable name is that we've created.
So, in this case, the counterReducer variable in store.js is a reference to the reducer function that was exported from the counterSlice.js file.
Related
Main problem: no datÄ™ in store from slice.
Description: I want to create shop. I used to RTKQ to fetch date from endpoint and slice to create shop cart (increase, decrease, remove, add etc).
Problem is that I can connect it to one working system. I read a lot that it is not recommended and you try to prevent from that thing. But I also read that this is possible.
I read the solution that I can put as extraReducers to slice, but I want know how to connect it by store.
import { configureStore } from "#reduxjs/toolkit";
import { setupListeners } from "#reduxjs/toolkit/dist/query";
import { DummyShopApi } from "./reducers/itemSlice";
import { cartSlice } from "./reducers/cartSlice";
import {reducerPath} from "./reducers/itemSlice";
import { combineReducers } from 'redux'
export const rootReducer = combineReducers({
card: cartSlice,
[DummyShopApi.reducerPath]: DummyShopApi.reducer
});
export const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(DummyShopApi.middleware),
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
setupListeners(store.dispatch);
it's common question and you could find the solution in documentation, but...))) I'll explain
When you have some query, what looks like:
export const pizzaApi = api.injectEndpoints({
endpoints: (builder) => ({
getPizzas: builder.Query<pizzasResponse, any>({
query: ({ sortBy }) => {
const sortBy1 = `sortBy=${sortBy ? sortBy : 'price'}`;
return {
url: `api?${sortBy1}`
};
}
})
})
});
export const { useGetPizzasQuery } = pizzaApi;
You get a 'queryHook' called useGetPizzasQuery. To add fetched pizzas from it to state (pizzaSlice), you should define extra reducer in your's slice:
extraReducers: (builder) => {
builder
.addMatcher(pizzaApi.endpoints.getPizzas.matchFulfilled, (state, action) => {
state.pizzas = action.payload.pizzas.content;
});
}
im trying to read some data from Redux store but I keep getting undefined
this is based on redux template created using:
create-react-app [appname] --template redux
I know there most be an issue in how I read the data
this is my store:
import { configureStore } from "#reduxjs/toolkit";
import calculatorReducer from "../features/Data_calculator_slice";
export const store = configureStore({
reducer: {
calculator: calculatorReducer,
},
});
my actions and initialState:
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
value: [{ name: "test", wight: 18, grade: 12, gpa: "A" }],
};
export const counterSlice = createSlice({
name: "data",
initialState,
reducers: {
incrementByAmount: (state, action) => {
state.value += action.payload;
const currentValue = selectCount();
console.log(currentValue);
},
},
});
export const { incrementByAmount } = counterSlice.actions;
export const selectCount = (state) => state.calculator.data;
/* export const selectCount = (state) => state.counter.value;
*/
export default counterSlice.reducer;
but here when I'm trying to fetch the data using the code below it returns undefined
const count = useSelector(selectCount);
console.log(count);
You cannot use the same selector in your reducers as you do in your component. One is scoped to the slice you're defining, the other is global. Also in your reducer you aren't passing the state to the selectCount selector so it can't return anything from the state.
in your reducer the selector for your value is (state) => state.value
in your component the selector for your value is (state) => state.calculator.value
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
I'm building a game using react-redux and TypeScript. I've got TS and React down, but redux... less so.
When the player clicks the start game button, the main menu is hidden via manipulation of redux state and the "gameState" is generated. "gameState" contains all the relevant information for the game world and entities therein, and is several thousand lines of serializable JSON. This part is definitely working, the problem comes when I try to dispatch to update it. I can see from the Redux browser extension that the payload being sent to the reducer function updateGameState is correct, but after the dispatch has been completed it's as if it never happened.
My question is simple: what am I doing wrong?
The code for the previously-mentioned dispatch is:
let nGS = gameStateGenerator.create(scene)
dispatch(updateGameState(nGS))
The layout of this part of the redux logic is as shown below. The four children of multiverse, universes, species, connections, and players, should all be populated, but are not.
I'm using combined reducers, as follows. I've not used ES6 notation for the reducer object properties as part of my attempts to rule out causes (which hopefully speaks to my level of desperation).
store.ts (top level)
import { configureStore, ThunkAction, Action } from '#reduxjs/toolkit';
import gameState from '../features/gameState/gameState';
export const store = configureStore({
reducer: {
gameState: gameState
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
gameState.ts (first and so far only child of root)
import { combineReducers, createSlice, PayloadAction } from '#reduxjs/toolkit';
import { GameState } from '../../interfaces/GameState';
import flags, { initialState as flagsInitialState} from '../flags/flags';
import multiverse, { initialState as multiverseInitialState } from '../multiverse/multiverse';
export const initialState: GameState = {
multiverse: multiverseInitialState,
flags: flagsInitialState
};
export const gameState = createSlice({
name: 'gameState',
initialState,
reducers: {
updateGameState: (state: GameState, action: PayloadAction<GameState>) => {
return Object.assign({}, state, action.payload)
}
}
});
export const { updateGameState } = gameState.actions;
export default combineReducers({
flags: flags,
multiverse: multiverse
})
flags.ts
import { createSlice } from '#reduxjs/toolkit';
import { FlagsState } from '../../interfaces/Flags';
export const initialState: FlagsState = {
ui: {
showMainMenu: true,
showWelcome: true,
gameStarted: false,
gameLoading: false,
gameLoaded: false
}
};
export const flags = createSlice({
name: 'flags',
initialState,
reducers: {
startGame: (state: Required<FlagsState>) => {
return Object.assign({}, state, {
ui: {
...state.ui,
showMainMenu: false,
gameStarted: true,
gameLoading: true
}
})
},
gameLoaded: (state: Required<FlagsState>) => {
return Object.assign({}, state, {
ui: {
...state.ui,
gameLoaded: true
}
})
}
}
});
export const { startGame, gameLoaded } = flags.actions;
export const getState = (state: FlagsState) => state;
export default flags.reducer;
And finally multiverse.ts
import { createSlice, PayloadAction } from '#reduxjs/toolkit';
import { Multiverse } from '../../interfaces/Multiverse';
import { Universe } from './../../interfaces/Universes';
export const initialState: Multiverse = {
universes: [],
species: [],
connections: [],
players: []
};
export const multiverse = createSlice({
name: 'multiverse',
initialState,
reducers: {
setUniverses: (state: Required<Multiverse>, action: PayloadAction<Universe[]>) => {
return Object.assign({}, state, { universes: action.payload })
}
}
});
export const { setUniverses } = multiverse.actions;
export const getState = (state: Multiverse) => state;
export default multiverse.reducer;
I'm think the trouble comes from your reducer, you use Object assign and return.
Redux-Toolkit uses Immer to change the state with no mutating like this :
(state, action) => state.value = action.payload
See the doc, https://redux-toolkit.js.org/usage/immer-reducers#immutable-updates-with-immer
So for you, you can do something like :
state.univers = {...state.univers, ... action.payload}
with no return.
I am adding and deleting items in an array using the createSlice() function from the redux-toolkit library.
The addProject reducer function works fine but the removeRpoject doesn't work.
//projects.js
import { createSlice } from "#reduxjs/toolkit";
let lastId = 0;
const slice = createSlice({
name: "projects",
initialState: [],
reducers: {
projectAdded: (projects, action) => {
projects.push({
id: ++lastId,
name: action.payload.name,
});
},
projectRemoved: (projects, action) =>
(projects = projects.filter((pro) => pro.id !== action.payload.id)),
},
});
export const { projectAdded, projectRemoved } = slice.actions;
export default slice.reducer;
//store.js
import { configureStore } from "#reduxjs/toolkit";
import reducer from "./projects";
const store = configureStore({ reducer: reducer });
export default store;
//index.js
import store from "./store/store";
import { projectAdded, projectRemoved } from "./store/projects";
const unsubscribe = store.subscribe(() => {
console.log("store Changed", store.getState());
});
store.dispatch(projectAdded({ name: "Project 1" }));
store.dispatch(projectRemoved({ id: 1 }));
You are replacing the root object of your state (projects) - this kills the immer change detection.
The simplest way is to just return the filtered array without assigning it to the draft object first:
projectRemoved: (projects, action) => projects.filter((pro) => pro.id !== action.payload.id),
See also:
https://immerjs.github.io/immer/docs/update-patterns#array-mutations