How to store action.payload in redux-toolkit - javascript

I want to set state.name to "Alex" and age plus one after click the Login function, and set email to action.payload.
Here is my user state slice
const userSlice = createSlice({
name: "user",
initialState: { value: { name: "", age: 0, email: "" } },
reducers: {
login: (state, action) => {
state.value.name = "Alex"
state.value.age = state.value.age + 1
state.value.email = action.payload
},
}
})
Here is my useDispatch
function Login() {
const dispatch = useDispatch()
return (
<div>
<button
onClick={() => {
dispatch(login({ email: 'example#gmail.com' }))
}}
>
Login
</button>
</div>
)
}
There is an error: Objects are not valid as a React child (found: object with keys {email}). If you meant to render a collection of children, use an array instead.
I am using Redux Toolkit.

It seems you are likely selecting this state.user.value state in a component and rendering the constituent parts, i.e. value.name, value.age, and value.email. This is fine until you update the state.user.value.email to have the payload value of { email: 'example#gmail.com' } when dispatching the login action. This makes the value.email an object and unrenderable.
Either dispatch the login action with the exact payload value you want to save into state:
<button
onClick={() => {
dispatch(login('example#gmail.com'));
}}
>
Login
</button>
or unpack the email property from the payload correctly in the case reducer:
const userSlice = createSlice({
name: "user",
initialState: {
value: {
name: "",
age: 0,
email: ""
}
},
reducers: {
login: (state, action) => {
state.value.name = "Alex";
state.value.age = state.value.age + 1;
state.value.email = action.payload.email;
},
}
});
I would suggest also simplifying the state to unnest the actual values you want to store.
Example:
const userSlice = createSlice({
name: "user",
initialState: {
name: "",
age: 0,
email: ""
},
reducers: {
login: (state, action) => {
state.name = "Alex";
state.age = state.age + 1;
state.email = action.payload; // { payload: 'example#gmail.com' }
},
}
});
...
const user = useSelector(state => state.user);
...
<>
Name: {user.name}
Age: {user.age}
Email: {user.email}
</>

Related

How to update redux state using onChange with React Redux

I'm using React Redux and want to be able to change the title and description of a post, using the onChange method. When only using React the way you would do this is that you keep an useState which you change whenever a change occurs, but I can't seem to get it to work with using redux in react. Instead of the state changing the original title, and description remains and cannot be changed.
From what I have read the basic idea is to have a listener on the input (onChange, usually) and have that fire a redux action. You then have the action tell the reducer to make the change to the store.
I have tried doing this, but could make it work correctly. What am I doing wrong and how do you solve it? I'm also wondering how do I specify that I want to change either title or description when using onChange, or do I simply send everything in post each time a change occurs?
This is what the redux state looks like when entering a post:
{
auth: {
isSignedIn: true,
user: {
id: '624481f22566374c138cf974',
username: 'obiwan',}
},
posts: {
'62448632b87b223847eaafde': {
_id: '62448632b87b223847eaafde',
title: 'hellothere',
desc: 'its been a long time since I heard that name...',
username: 'vorbrodt',
email: 'example#gmail.com',
categories: [],
createdAt: '2022-03-30T16:32:50.158Z',
updatedAt: '2022-03-30T16:32:50.158Z',
__v: 0
}
},
}
Here is where the onChange happens.
Post.js
import { getPostById, editPost } from "../actions";
const Post = ({ getPostById, editPost, username }) => {
const [updateMode, setUpdateMode] = useState(false);
let { id } = useParams();
let post = useSelector((state) => state.posts[id]);
const handleInputChange = (e) => {
try {
editPost(e.target.value);
} catch (err) {}
};
return (
<div className="post">
<div className="post-wrapper">
{updateMode ? (
<input
type="text"
value={post.title}
className="post-title-input"
autoFocus
onChange={(e) => handleInputChange(e)}
/>
) : (
<h1 className="post-title">
{post.title}
</h1>
)}
<div className="desc-area">
{updateMode ? (
<textarea
className="post-desc-input"
value={post.desc}
onChange={(e) => handleInputChange(e)}
/>
) : (
<p className="post-desc">{post.desc}</p>
)}
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return { username: state.auth.user.username };
};
export default connect(mapStateToProps, { getPostById, editPost })(Post);
Here is the action creator:
//edit post in redux state
const editPost = (postValues) => (dispatch) => {
dispatch({ type: EDIT_POST, payload: postValues });
};
And here is the reducer which is suppose to change the state.
postReducer.js
import _ from "lodash";
import { GET_POSTS, GET_POST, CREATE_POST, EDIT_POST } from "../actions/types";
function postReducer(state = {}, action) {
switch (action.type) {
case GET_POSTS:
return { ...state, ..._.mapKeys(action.payload, "_id") };
case GET_POST:
return { ...state, [action.payload._id]: action.payload };
case CREATE_POST:
return { ...state, [action.payload._id]: action.payload };
case EDIT_POST:
//here the change should occur, not sure how to specify if title or desc should
//change
return { ...state, [action.payload._id]: action.payload };
default:
return state;
}
}
export default postReducer;
Hey there something like this should be of help
const handleInputChange = (e, key, id) => {
try {
editPost({ [key]: e.target.value, id });
} catch (err) {}
};
Usage
<textarea
className="post-desc-input"
value={post.desc}
onChange={(e) => handleInputChange(e, "title", post.id)}
/>
action
const editPost = (postValues) => (dispatch) => {
dispatch({ type: EDIT_POST, payload: postValues });
};
Reducer
case EDIT_POST:
//here we destructure the id and return the data without the id cause we //need it below
const {id, ...newData} = action.payload
const indexToUpdate = state.posts.find(post => post.id === id)
const newPostsData = [...state.posts]
//Here we update the actual object and its property that is in the state at //the specific value
newPostsData[indexToUpdate] = {...newPostData[indexToUpdate], {...newData}
return { ...state, posts: newPostsData};

State of my application is not working properly

I am creating delete functionality in my application. But somehow it is not behaving properly.
The initial state and reducer function is defined below:
import { createSlice } from '#reduxjs/toolkit';
const filterDataTemplate = {
programId: '',
year: '',
};
const initialState = {
//some other state
filterData: { ...filterDataTemplate },
};
const slice = createSlice({
name: 'editFilterSlice',
initialState: initialState,
reducers: {
updateFilterProgramId: (state, action) => {
return {
...state,
filterData: {
...state.filterData,
programId: action.payload,
},
};
},
updateFilterYear: (state, action) => {
return {
...state,
filterData: {
...state.filterData,
year: action.payload,
},
};
},
},
});
export const {
updateFilterYear,
updateFilterProgramId,
} = slice.actions;
export default slice.reducer;
And filter details is the object containing year and programId:
filterDetails: {year:2021, programId: "Ameria"}
And i want to achieve
filterDetails: {year:"", programId: ""}
So delete handler is stated below to achieve above criteria:
const handleDelete = (e) => {
e.preventDefault();
if (//some condition) {
dispatch(updateFilterYear(''));
console.log('filterdetails', filterDetails);
//filterDetails: {year:2021, programId: "Ameria"}
} else {
dispatch(updateFilterProgramId(''));
}
}.
//Outside handleDelete function
console.log('filterdetails', filterDetails);
//filterDetails: {year:"", programId: "Ameria"}
So filter details containg year and programId is obtained with the help of this code:
const filterDetails = useAppSelector(
(state) => state.locationsFilter.filterData
);
handleDelete function is getting called properly when I am clicking a button
But after running this code, the filter details is not updating first time but when I am again running the handler second time it is updating.
And outside handleDelete it is showing correct value in the console but inside handleDelete console is showing correct value second time.
It is showing very strange behavior

Player's age is not edited

So in my program, I am statically creating and removing players. However, the editing of a players's attribute isn't working.
I have 3 action types. ADD_PLAYER, REMOVE_PLAYER, EDIT_PLAYER
For each one of them, I create an action generator, which contains a type and any other parameters accordingly. Here is my full code
import { createStore, combineReducers } from 'redux';
import uuid from 'uuid';
// ADD_PLAYER
const addPlayer = (
{
firstName = '',
lastName = '',
age = 0,
position = ''
} = {}) => ({
type: 'ADD_PLAYER',
player: {
id: uuid(),
firstName,
lastName,
age,
position
}
});
// REMOVE_PLAYER
const removePlayer = ( {id} = {} ) => ({
type: 'REMOVE_PLAYER',
id
});
// EDIT_PLAYER
const editPlayer = (id, updates) => ({
type: 'EDIT_PLAYER',
id,
updates
})
const playersReduxDefaultState = [];
// The Player reducer
const playersReducer = (state = playersReduxDefaultState, action) => {
switch(action.type) {
case 'ADD_PLAYER':
return [
...state,
action.player
]
case 'REMOVE_PLAYER':
return state.filter(({id}) => id !== action.id)
case 'EDIT_PLAYER':
return state.map((player) => {
if(player.id === action.id) {
return {
...player,
...action.updates
}
} else {
return player
}
});
default:
return state;
}
};
const store = createStore(
combineReducers({
players: playersReducer
})
)
store.subscribe(() => {
console.log(store.getState())
});
store.dispatch(
addPlayer(
{
firstName: 'Theo',
lastName: 'Tziomakas',
age: 38,
position: 'Striker'
}
))
const playerOne = store.dispatch(
addPlayer(
{
firstName: 'Vasilis',
lastName: 'Tziastoudis',
age: 38,
position: 'Defender'
}
))
store.dispatch(removePlayer({ id: playerOne.player.id}));
// Edit player's one age.
store.dispatch(editPlayer(playerOne.player.id, { age: 29 }));
What am I missing?
Thanks,
Theo.

Item not deleting from array using Redux

I am following a tutorial trying to learn Redux. I got the first action working, which is a simple GET API call, but am stuck on the next action I'm trying to create. The code looks like the following:
In the Component:
class ShoppingList extends Component {
componentDidMount() {
this.props.getItems();
}
handleClick = id => {
console.log("component " + id);
this.props.deleteItem(id);
};
render() {
const { items } = this.props.item;
return (
<Container>
<ListGroup>
<TransitionGroup className="shoppingList">
{items.map(({ id, name }) => (
<CSSTransition key={id} timeout={500} classNames="fade">
<ListGroupItem>
<Button
className="button1"
color="danger"
size="sm"
onClick={e => this.handleClick(id, e)}
>
×
</Button>
{name}
</ListGroupItem>
</CSSTransition>
))}
</TransitionGroup>
</ListGroup>
</Container>
);
}
}
ShoppingList.propTypes = {
getItems: PropTypes.func.isRequired,
item: PropTypes.object.isRequired,
deleteItem: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
item: state.item
});
export default connect(mapStateToProps, { getItems, deleteItem })(ShoppingList);
In my reducer:
const initialState = {
items: [
{ id: 3, name: "Eggs" },
{ id: 4, name: "Milk" },
{ id: 5, name: "Steak" },
{ id: 6, name: "Water" }
]
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_ITEMS:
return {
...state
};
case DELETE_ITEM:
console.log("reducer");
return {
...state,
items: state.items.filter(item => item.id !== action.id)
};
default:
return state;
}
}
In my actions file:
export const getItems = () => {
return {
type: GET_ITEMS
};
};
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
payload: id
};
};
However, when I click on the button to try to delete an item from the list, nothing happens. I can see in the Redux console that the action is being dispatched, however it seems to have no effect. Any suggestions?
You have in deleteItem action { type, payload }. Instead you can have { type, id } or using payload in the reducer return statement.
I would do the following - so you are passing the id with the action instead of payload:
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
id
};
};
Or the best option for later purposes - keep payload just adding id as property:
// action
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
payload: { id }
};
};
// reducer
case DELETE_ITEM:
// here destructuring the property from payload
const { id } = action.payload;
return {
...state,
items: state.items.filter(item => item.id !== id)
};
I hope this helps!

How to pass value from tcomb-form-native to reducer? React + redux

In my project I'm using tcomb-form-native library to validation a form. Redux working fine but I can't pass value of inputs to reducer. I have to do this, because I want to create array with data from fields.
How can I pass values of my inputs to reducer?
Or maybe it's not possible with this library and I have to use another one?
Form.js
const mapDispatchToProps = dispatch => {
return {
closeExpenseDialog: (value) => dispatch({type: 'CLOSE_EXPENSE_DIALOG'}),
};
};
const mapStateToProps = state => {
return {
value: state.closeExpenseDialog.value,
};
};
const Form = t.form.Form;
const Expense = t.struct({
expense: t.String,
cost: t.Number
});
const options = {
fields: {
expense: {
error: 'This field is required'
},
cost: {
error: 'This field is required'
}
}
};
handleClick = () => {
const value = this._form.getValue();
if (value) {
console.log(value);
this.props.closeExpenseDialog(value);
} else {
console.log('validation failed');
}
}
<Form
type={Expense}
ref={c => this.props._form = c}
options={options}
value={this.props.value}
/>
<ActionButton
onPress={this.props.closeExpenseDialog}
title={title}
/>
Reducer.js
const initialState = {
value: {}
};
const mainReducer = (state = initialState, action) => {
switch (action.type) {
case 'CLOSE_EXPENSE_DIALOG':
console.log('it works')
console.log(state.value) //undefined
default:
return state;
}
};
I needed add onChange() attribute and use it to pass object value to the Reducer.
Form.js
const Expense = t.struct({
expense: t.String,
cost: t.Number
});
const options = {
fields: {
expense: {
error: 'This field is required'
},
cost: {
error: 'This field is required'
}
}
};
handleClick = () => {
const value = this._form.getValue();
if (value) {
this.props.submitExpenseDialog();
}
}
<Form
type={Expense}
ref={c => this._form = c}
options={options}
value={this.props.value}
onChange={this.props.changeExpenseInputs}
/>
<ActionButton
onPress={this.handleClick}
title={title}
/>
Reducer.js
const initialState = {
value: {}
};
const mainReducer = (state = initialState, action) => {
switch (action.type) {
case 'SUBMIT_EXPENSE_DIALOG':
console.log(state.value)
default:
return state;
}
};

Categories