I have an input field where I am trying to pass some information before moving onto a separate page. My problem is the Redux state is not changing, but the console is showing the value is being passed correctly. I'm assuming something is wrong with my Slice but I believe I am passing the payload correctly. My Redux slice looks like:
import { createSlice } from "#reduxjs/toolkit";
export const walletSlice = createSlice({
name: "wallet",
initialState: {
wallet: "xxx-xxxx-xxx-xxxx",
},
reducers: {
setWalletAddress: (state, action) => {
state.value = action.payload;
},
},
});
export const { setWalletAddress } = walletSlice.actions;
export default walletSlice.reducer;
While my from component looks like:
import { setWalletAddress } from "../../redux/wallet";
import { useDispatch } from "react-redux";
export default function AddressForm() {
return (
const dispatch = useDispatch();
const handleChangeWallet = (event) => {
dispatch(setWalletAddress (event.target.value));
console.log(event.target.value);
};
<React.Fragment>
<TextField
onChange={handleChangeWallet}
label="Wallet address"
/>
</React.Fragment>
);
}
My store looks pretty standard:
export default configureStore({
reducer: {
wallet: walletReducer,
},
});
I assume that you need to use correct state field name in the reducer function. I guess following code line makes an issue,
state.value = action.payload;
Instead of this, you need to write correct field name wallet not value
state.wallet = action.payload;
You've mistyped value instead of wallet
setWalletAddress: (state, action) => {
state.wallet = action.payload;
},
Related
I'm trying to use Redux to set a boolean in the state to show/hide an element.
Whenever I dispatch a boolean the following error shows:
A non-serializable value was detected in the state, which is weird as a boolean is perfectly serializable?
Todo.js
import { useSelector, useDispatch } from "react-redux";
export default function Planner() {
const dispatch = useDispatch();
function createHandleClickOpen() {
dispatch(createDialogOpen(true));
}
createHandleClickOpen is bound on a button's onClick() method.
Plannerstore.js
import { createSlice } from "#reduxjs/toolkit";
export const plannerSlice = createSlice({
name: "planner",
initialState: {
createDialogOpen: false,
},
reducers: {
createDialogOpen: (state) => (value) => {
state.createDialogOpen = value;
},
},
});
// Action creators are generated for each case reducer function
export const { createDialogOpen } = plannerSlice.actions;
export default plannerSlice.reducer;
Store.js
import { configureStore } from "#reduxjs/toolkit";
import plannerSlice from "../../feature/planner/plannerStore";
export default configureStore({
reducer: {
planner: plannerSlice,
},
});
You have a mistake in your reducer.
You should use the action payload to retrieve the value you dispatch:
export const plannerSlice = createSlice({
name: "planner",
initialState: {
createDialogOpen: false,
},
reducers: {
createDialogOpen: (state, action) => {
state.createDialogOpen = action.payload;
},
},
});
The specific issue here is that the reducer is written incorrectly.
You have:
createDialogOpen: (state) => (value) => {
state.createDialogOpen = value;
},
That means that it's "a function that takes state as an argument, and returns a function that takes value as an argument".
The result of a reducer is what it returns (or in the case of RTK + Immer, what you "mutate" in state). So, the problem here is that you are returning a function, and functions are not serializable.
The other issue is that we don't use value to describe any of the arguments to a reducer. The arguments are (state, action), and with createSlice, what normally matters is the action.payload field.
As noted in the other comment, the correct syntax would be:
createDialogOpen: (state, action) => {
state.createDialogOpen = action.payload;
},
I'm learning redux, and i've a method addPosts to add posts to the list of posts, and I'm doing it like this.
import { createSlice } from "#reduxjs/toolkit";
var initialState = [{ number: 1 }, { number: 2 }, { number: 3 }, { number: 4 }];
export const postsSlice = createSlice({
name: "postsSlice",
initialState,
reducers: {
addPost: (state, action) => {
state = [...state, action.payload];
},
},
});
export const allPosts = (state) => state.posts;
export const { addPost } = postsSlice.actions;
export default postsSlice.reducer;
and using the state like this.
import { useSelector, useDispatch } from "react-redux";
import { addPost, allPosts } from "./postsSlice";
function Posts() {
var posts = useSelector(allPosts);
var dispatch = useDispatch();
return (
<div>
{posts.map((post) => (
<div>{post.number}</div>
))}
{/* add post */}
<button
onClick={() => {
dispatch(addPost({ number: 1 }));
console.log(posts);
}}
>
addpost
</button>
</div>
);
}
export default Posts;
using state.push(action.payload) works somehow, altough the documentation says not use update state like this, and update in an immutable way.
like this state = [...state, action.payload]. it does not update state with this immutable way.
I don't know what is wrong that i'm doing.
thanks in advance for any help
You are misreading the wrong documentation for the wrong tool it seems - in a Redux Toolkit createSlice reducer, it is always 100% correct to use something like state.push to mutably modify the object in the state variable.
What you cannot do however is what you are trying here: reassign the state variable. That had never any effect in any kind of Redux reducer, unless you would return that state variable later.
If you want to do that, you will need to return [...state, action.payload] instead and leave the state variable alone altogether - it should not be reassigned.
But the recommended way would be that push.
For more, please read Writing Reducers with Immer
As per this instead of directly changing into state you can return in this way
return [...state, action.payload]
Depending on your definition of initialState
Please have a look into working example of react-redux-toolkit-slice-example
Below is the definition of slice
import { createSlice } from "#reduxjs/toolkit";
const initialState = [{ number: 1 }];
export const postsSlice = createSlice({
name: "postsSlice",
initialState,
reducers: {
addPost: (state, action) => {
return [...state, action.payload];
}
}
});
export const allPosts = (state) => state.posts || [];
export const { addPost } = postsSlice.actions;
export default postsSlice.reducer;
Defining the reducer(postSlice) in store
import { configureStore } from "#reduxjs/toolkit";
import postsReducer from "../features/posts/postsSlice";
export default configureStore({
reducer: {
posts: postsReducer
}
});
Use of slice in component
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { addPost, allPosts } from "./postsSlice";
const Posts = () => {
var posts = useSelector(allPosts);
var dispatch = useDispatch();
return (
<div>
{posts.map((post, key) => (
<div key={key}>{post.number}</div>
))}
{/* add post */}
<button
onClick={() => {
dispatch(
addPost({
number: Math.max(...posts.map(({ number }) => number)) + 1
})
);
console.log(posts);
}}
>
Add Post
</button>
</div>
);
};
export default Posts;
Basically I got a state called optimizer in which I store a field named optimizer_course_entries , this field has 2 reducers on it:
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
optimizer_course_entries : [],
}
export const optimizerSlice = createSlice(
{
name: 'optimizer',
initialState,
reducers: {
edit_entry: (state, action) => {
console.log('CALLED EDIT REDUCERS');
state.optimizer_course_entries[action.payload.index] = action.payload.value;
},
reset: (state) => {
console.log('CALLED RESET REDUCER');
state.optimizer_course_entries = [];
}
}
}
)
export const {edit_entry, reset} = optimizerSlice.actions;
export default optimizerSlice.reducer;
In my react app, I have a call to edit_entry everything a textbox is edited, and it sends the index and value in a payload to Redux.
const receiveChange = (Value, Index) => {
dispatch(edit_entry({
index : Index,
value : Value,
}));
}
I have the reset reducer set on component mount like this:
React.useEffect(
() => {
dispatch(reset());
} , []
)
The issue i'm having is that on component mount, instead of redux only doing a reset, it also restores previous reducer actions..
And in my redux store, the optimizer_course_entries entry is identical to before the reset...
I'm still pretty new to redux, is there a way I can specify it so that upon re-mount it doesn't do this repopulation?
Not able to access the redux store current state in a Class component.
It shows up console error
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
When I tried to implement the same using a function component with useSelector and useDispatch, everything works as expected. What has gone wrong over here?
reducer.js
let initialState={
count:0
}
const reducer=(state=initialState,action)=>{
switch(action.type){
case ADD_INCREMENT:
return {
...state,
count:state.count+1
};
default: return state;
}
}
export default reducer;
action.js
const Increment=()=>{
return {
type:ADD_INCREMENT
}
}
store.js
import reducer from './reducer';
const store=createStore(reducer);
export default store;
Class Component
import { connect } from 'react-redux';
const mapStateToProps=state=>{
return {
count:state.count
}
}
const mapDispatchToProps=(dispatch)=>{
return {
count:()=>dispatch(action.Increment())
}
}
class Orders extends Component {
render() {
return (
<div>
<h1>Count: {this.props.count} </h1>
</div>
);
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Orders);
In App.js the entire container is wrapped with Provider and store is passed as props
Issue
You've named your state and your action both count, the latter is the one injected as a prop.
const mapStateToProps = state => {
return {
count: state.count // <-- name conflict
}
}
const mapDispatchToProps = (dispatch) => {
return {
count: () => dispatch(action.Increment()) // <-- name conflict
}
}
Solution
Provide different names, count for the state, maybe increment for the action.
const mapStateToProps = state => ({
count: state.count,
});
const mapDispatchToProps = (dispatch) => ({
increment: () => dispatch(action.Increment())
})
I'm trying to pass information from the initial state of store to a component where it's rendered, but it's not showing. A console.log in the component itself showed that it was undefined. There is nothing wrong with the initial state, I can access it using a console.log statement in App.tsx, so I suspect it's got something to do with passing it down as a prop or it needs initialization with componentDidMount or similar.
reducers.tsx:
import { combineReducers } from 'redux';
import {
TaskListState,
TaskActionTypes,
ITask,
ADD_TODO
} from './types'
const initialState:TaskListState = {
tasks: [
{
name: "testing123",
done: false,
}
]
}
export function taskReducer(state = initialState, action: TaskActionTypes)
: TaskListState {
switch(action.type){
case ADD_TODO:
let newTask:ITask = {
name: action.name,
done: false
}
return {
tasks: [...state.tasks, newTask]
}
default:
return state
}
}
//create another reducer for the filtering, then combine the reducers
const TaskList = combineReducers({
taskReducer
})
export default TaskList
GetTask.tsx:
import { connect } from 'react-redux'
import { TaskListState } from '../../redux/tasks/types'
import { Tasks } from '../tasks/Tasks'
const mapStateToProps = (state:TaskListState) => ({
tasks: state.tasks
})
const mapDispatchToProps = {
}
export const Connector = connect(mapStateToProps, mapDispatchToProps)(Tasks)
Tasks.tsx:
import { ITask } from '../../redux/tasks/types'
import React from 'react';
import './Tasks.css';
type Props = {
tasks: ITask[];
}
export const Tasks: React.FC<Props> = (props:Props) => {
const { tasks } = props;
console.log(tasks);
return (
<div>
{ tasks }
</div>
)
}
When you pass the taskReducer to combineReducers using object property shorthand, your reducer will be named taskReducer in the store, your store looks like this
const store = {
taskReducer: {
tasks: [{
name: "testing123",
done: false,
}]
}
}
So when you try to select tasks in mapStateToProps, state.tasks is undefined
The type of the root state is not TaskListState, to get the type of your store use ReturnType
type RootState = ReturnType<typeof TaskList>
And finally change the path to your tasks list in mapStateToProps together with the new type of the RootState which will prevent this kind of errors in the future
const mapStateToProps = (state: RootState) => ({
tasks: state.taskReducer.tasks
})
I just breifly check your script and i think your reducer function is wrong
export function taskReducer(state = initialState, action: TaskActionTypes)
: TaskListState {
switch(action.type){
case ADD_TODO:
let newTask:ITask = {
name: action.name,
done: false
}
return {
// tasks: [...state.tasks, newTask]
[...state.tasks, newTask]
}
default:
return state
}
}
I hope it will works for you.
In the below line
export const Connector = connect(mapStateToProps, mapDispatchToProps)(Tasks)
you are adding the Tasks component but you are not passing a prop called tasks thats why it is showing as undefined try rendering the component in the same file
Or you can do it like this
mapStateToProps and mapDispatchToProps both take ownProps as the second argument.
[mapStateToProps(state, [ownProps]): stateProps] (Function):
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function):
For reference here