redux "undefined" when trying to get the data - redux

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

Related

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

Redux dispatch fires with correct payload, but state is not updated

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.

How counterReducer is generated in Redux tutorial sample

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.

How can I delet a single element from the array in redux state using redux toolkit

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

Resources