I'm practicing Redux and in my project when i dispatch an action i will see that in the dev tools but the state won't change and it will remain an empty Array. why is this happening ?
i will appreciate a little help
shopping cart.js
import { createSlice, createAction } from "#reduxjs/toolkit";
// Action Creator
export const itemAdd = createAction("products/itemAdded");
const slice = createSlice({
name: "shoppingCart",
initialState: [],
reducers: {
itemAdded: (cart, action) => cart.push(action.payload),
},
});
export const { itemAdded} = slice.actions;
export default slice.reducer;
Dispatch
import { useDispatch, useSelector } from "react-redux";
import { itemAdd } from "../../store/state/shoppingCart";
// It's an onClick Event Handler
const handleAddItem = (item) => {
dispatch(itemAdd(item));
};
Also, your reducer needs braces.
itemAdded: (cart, action) => { cart.push(action.payload) }
Otherwise it will return the return value of cart.push, which is the new length of the array - and immer can't do its work if you modify the state and return something unrelated at the same time.
Because you are importing itemAdd
import { itemAdd } from "../../store/state/shoppingCart";
instead of itemAdded
import { itemAdded } from "../../store/state/shoppingCart";
Related
I'm using React and redux-toolkit and I have a question.
Can I change one property of a certain state and the other property of it at the same time?
Let's say...
Here is testSlice.js. It has user property and items property. Once user property change, items property have to change. (That means each user has different Items)
testSlice.js
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
user:{id:"",name:""},
items: []
}
const testSlice = createSlice({
name: 'test',
initialState,
reducers: {
changeUser(state, action) {
state.user = { ...action.payload };
},
renewItems(state, action) {
state.items = [...action.payload];
}
},
})
export const {changeUser, renewItems} = testSlice.actions;
export const selectTest=(state =>state.test);
export default testSlice.reducer;
Here is TestPage.jsx. It has the button to dispatch 2 actions. The one is to change user proeprty and the other is to renew items property that user have.
TestPage.jsx
import { useDispatch, useSelector } from 'react-redux';
import { changeUser, renewItems,selectTest} from '../slice/testSlice';
import { Button } from '#mui/material';
import React from 'react';
import store from '../store';
export const TestPage = () => {
const getTest = useSelector(selectTest);
const dispatch = useDispatch();
const testPach = () => {
dispatch(changeUser());
dispatch(renewItems());
};
console.log(store.getState());
return (
<>
<div>{getTest.user.name}</div>
<Button variant="contained" onClick={() => testPach()}>testSlice用</Button>
</>
);
};
Like this, is it ok to change state? I'm not sure, so is it anyone who are familiar with it? Thank you in advance.
SO im having a hard time understanding reducers and dispatch, and useSelectors.
In this example, im clicking a button via onRowItemClick. This should in theory, dispatch an action, which is playAudio.
That action is inside a reducer? And i would imagine it should console.log an empty array, which is the current state. However, when i click it, nothing happens in the console.
Slice:
import { createSlice } from '#reduxjs/toolkit';
const initialState = [];
const AudioSlice = createSlice({
name: 'AudioSlice',
initialState,
reducers: {
playAudio(state){
console.log(current(state));
}
},
});
export const { playAudio} = AudioSlice.actions;
export default AudioSlice.reducer;
The dispatch:
import React from 'react';
import { useDispatch } from 'react-redux';
import { playAudio } from 'slices/AudioSlice';
const ProjectPageAudioTab = () => {
const dispatch = useDispatch();
const onRowItemClick = () => {
dispatch(playAudio());
};
}
export default ProjectPageAudioTab;
Following this tutorial: https://www.youtube.com/watch?v=iBUJVy8phqw
I would like to set up REdux in my Next.js app. But I got stuck. Do not know why. Everything is the same as in the tutorial.
counter.js:
import { createSlice } from "#reduxjs/toolkit";
export const counterSlice = createSlice({
name: "counter",
initialState: {
count: 0,
},
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.count += 1;
},
decrement: (state) => {
state.count -= 1;
},
incrementByAmount: (state, action) => {
state.count += action.payload;
},
},
});
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
store.tsx:
import { configureStore } from "#reduxjs/toolkit";
import { counterReducer } from "./counter";
export default configureStore({
reducer: {
counter: counterReducer,
},
});
first part in onboarding2.tsx:
import Head from "next/head";
import Image from "next/image";
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
export const fallBackLang = "hu";
interface Onboarding2Props {
lang: string;
translations: Translation;
}
export default function Onboarding2(props: Onboarding2Props) {
const { lang, translations } = props;
const { count } = useSelector((state) => state.counter);
You have attempted to import 'counterReducer' which is not present in counter.js
'counterReducer' is ought to be the counterSlice.reducer which is exported as default from counter.js
You could simply replace
import { counterReducer } from "./counter";
with
import counterReducer from "./counter";
in your store.js file
I had a similar issue and the solution was to add the reducer to the store:
...
reducer: {count: countReducer}
...
Take a look at the following slice I've created in TypeScript (storeSlice.ts):
import { createSlice, PayloadAction } from "#reduxjs/toolkit";
import axios from "axios";
import { AppThunk } from "../../app/store";
import { Summoner } from "../../models/Summoner";
export interface StoreState {
summoners: Summoner[]
}
const initialState: StoreState = {
summoners: []
}
export const storeSlice = createSlice({
name: 'store',
initialState,
reducers: {
getSummonersSuccess: (state: StoreState, action: PayloadAction<Summoner[]>) => {
state.summoners = action.payload;
}
}
});
export const { getSummonersSuccess } = storeSlice.actions;
export const getSummoners = (): AppThunk => (dispatch) => {
axios.get("api/summoners").then((response) => {
dispatch(getSummonersSuccess(response.data));
});
}
export default storeSlice.reducer;
It's pretty basic. It has an initial state that is of type StoreState which contains an array of type Summoner.
Now, the component that uses this slice looks like this (Store.tsx):
import React, { Dispatch } from 'react';
import { connect } from 'react-redux';
import { getSummoners, StoreState } from './storeSlice';
interface PropState {
store: StoreState
}
const Store = (props: StoreState) => {
console.log("store props", props); //This contains my "getSummoners" action, but I can't access it because it's not defined in "StoreState"
return (
<h1>Hello!</h1>
);
}
const mapStateToProps = (state: PropState) => {
console.log("Store state", state);
return { summoners: state.store.summoners };
};
const mapDispatchToProps = (dispatch: Dispatch<any>) => {
return {
getSummoners: dispatch(getSummoners)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Store);
When I log the props in the component, I get the state that I've mapped with mapStateToProps, and I also get the getSummoners action that I've mapped with mapDispatchToProps.
However, I cannot actually access the getSummoners action because it is not defined in StoreState.
I do not want to have to define the actions in every default state type that I create.
Is there something I can do to be able to use the dispatch actions in my code whilst keeping TypeScript happy?
Apologies if this question doesn't make too much sense, I'm fairly new to this tech stack.
In your mapDispatchToProps you are inadvertanly calling the dispatch rather than returning a function to call the dispatch from your component. Change it to:
const mapDispatchToProps = (dispatch: Dispatch<any>) => {
return {
getSummoners: () => dispatch(getSummoners)
}
}
This should clear up your problems. If it doesn't, please update your post with the exact error that you are getting.
As a sidenote, this connect higher-order component with mapStateToProps and mapDispatchToProps has been around for a while and it used to be the only way to get data from the state into your component. It still works, but nowadays it is recommended that you use the newer hooks useSelector and useDispatch instead.
I'm creating a simple blog using React and Redux, most for learning these two libraries. Everything is working fine, my only question is about the way the state object is structured in the application. When I go to the Redux Toolkit, I see this:
Redux Toolkit screenshot
In the state I have a post object with another post object inside it. My question is: where did I defined the state in that way (with a post object inside a post object)? Below are the content of this application:
MainPost.js
import React, { useEffect } from 'react'
import { connect, useDispatch } from 'react-redux'
import { getPostListAction } from '../store/actions/getPostListAction'
export const MainPost = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getPostListAction())
})
return (
<div>App</div>
)
}
const mapStateToProps = state => {
return {
post: state.post
}
}
export default connect(mapStateToProps)(MainPost)
store.js
import { createStore, applyMiddleware, compose } from 'redux'
import rootReducer from './reducers/rootReducer'
import { devToolsEnhancer } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import { api } from './middleware/api'
const store = createStore(
rootReducer,
compose(
devToolsEnhancer(),
applyMiddleware(thunk),
)
)
export default store
postListReducer.js
const initialState = {
post: ''
}
export default function getPostListReducer(state = initialState, action) {
if(action.type === 'getPostList') {
return {
...state,
post: action.payload
}
} else {
return state
}
}
The first post (after state) is namespace of postListReducer
Here is how you use combineReducer to create a rootReducer:
const rootReducer = combineReducers({
post: postListReducer,
other: otherReducer
})
And to select data from the store, you do:
const mapStateToProps = state => {
return {
post: state.post.post // the first "post" (after "state") is namespace of postListReducer
}
}
Or if you don't want to write state.post.post, you can change your postListReducer to directly hold the "post" data:
const initialPost = ''
export default function getPostListReducer(state = initialPost, action) {
if(action.type === 'getPostList') {
return action.payload
} else {
return state
}
}