update value of an element of object array in redux store - redux

There is a challenge update existing elements value in json array in redux store by an action creater.
You can run code here also shared it below;
console.clear()
const CreateTeam = (team, point) => {
return {
type:"CREATE_TEAM",
payload: {team, point}
}
}
const UpdateTeam = (team, point) => {
return {
type:"UPDATE_TEAM_POINT",
payload: {team, point}
}
}
const TeamReducer = (state = [], action) => {
if(action.type == "CREATE_TEAM")
{
return [...state, action.payload]
}
if(action.type == "UPDATE_TEAM_POINT")
{
let point=action.payload.point;
return [...state, {
...state.teams,
point:point
}]
}
return state;
}
const { createStore, combineReducers } = Redux;
const league = combineReducers({
teams: TeamReducer
})
const store = createStore(league);
store.dispatch(CreateTeam("TeamA",10));
store.dispatch(CreateTeam("TeamB",20));
store.dispatch(UpdateTeam("TeamA",15));//not work
console.log(store.getState())
create actions works fine, I expected the point value of TeamA set to 15.. but its added new object has only "point" property value 15

There is an error in name of actionTypes:
action dispatches type:"UPDATE_TEAM"
reducer handles action.type == "UPDATE_TEAM_POINT"
You have to perform immutable change, try this:
const TeamReducer = (state = [], action) => {
if(action.type == "CREATE_TEAM")
{
return [...state, action.payload]
}
if(action.type == "UPDATE_TEAM")
{
const {team, point} = action.payload;
const changedIdx = state.findIndex((item) => item.team === team);
return [...state.slice(0, changedIdx), action.payload, ...state.slice(changedIdx + 1)]
}
return state;
}

Related

Why filter method in my reducer returns an array of proxy? -Redux Toolkit

so i want to delete an item from array, onClick but when i log the filtered data in the console i get an array of Proxy.
i tried Changing my code but nothing worked
whats wrong here in itemRemoved?
import { createSlice, createAction } from "#reduxjs/toolkit";
// Action Creater
const slice = createSlice({
name: "shoppingCart",
initialState: [],
reducers: {
itemAdded: some code // ,
itemRemoved: (cart, { payload }) => {
cart.filter((item) => {
if (item.id === payload.id) {
if (item.count === 1) {
return cart.filter((item) => item.id !== payload.id);
}
else {
const itemIndex = cart.indexOf(item);
cart[itemIndex].count = cart[itemIndex].count - 1;
return cart;
}
}
});
},
},
});
export const { itemAdded, itemRemoved } = slice.actions;
export default slice.reducer;
Assuming you want to remove the element with the id you are passing through the dispatch function
itemRemoved: (state, { payload }) => {
const newCart = state.cart.filter(item => item.id !== payload.id)
const state.cart = newCart
return state
}),

Why In redux Output is coming like this --> State Changed{}

I am learning redux with simple age Increment and Decrement example here is the code
const { createStore } = require(`redux`);
const initialState = {age: 21};
const reducerOne = (state = initialState, action) => {
const newState = {...state};
if(action.type === `ADD`) {
newState.age = action.value;
}
if(action.type === `SUBTRACT`) {
newState.age = action.value;
}
return newState;
}
const store = createStore(reducerOne);
store.subscribe(() => {
console.log(`State Changed` + JSON.stringify(store.getState()));
})
store.dispatch({type: `ADD`, val: 10});
store.dispatch({type: `SUBTRACT`, val: 5});
But in Output it is showing like this --> State Changed{}
Help how to fix this and to get Output
Your action is posting the val property and your reducer is reading the value property.
Change your actions this way and it will work:
store.dispatch({type: `ADD`, value: 10});
store.dispatch({type: `SUBTRACT`, value: 5});
EDIT:
You are replacing your old age value, but probably you want to add or subtract from it. You have to modify your reducer to achieve such behaviour:
const reducerOne = (state = initialState, action) => {
const newState = {...state};
if(action.type === `ADD`) {
// add to the previous `age` value and do not overwrite it
newState.age = state.age + action.value;
}
if(action.type === `SUBTRACT`) {
// subtract from the previous `age` value and do not overwrite it
newState.age = state.age - action.value;
}
return newState;
}

Failed to store data in redux store

.created a store
.created actions and reducers
.connected store to my component
.failed to store data in redux store
i have to store the add events in a calendar using redux ,the values are not stored in redux .
// Actions
export const saveUserEvents=(events)=>{
return {
type:'SAVE_USER_EVENTS',
payload: events
}
}
//Reducers
export const form=(state = initialState, action) => {
switch (action.type) {
case "SAVE_USER_EVENTS":
return {...state, events: action.payload};
default:
return state
}
}
// Store
let reducers=combineReducers({
form
})
export default createStore(
reducers,
composeWithDevTools(
applyMiddleware(thunk)
)
)
// Component
Form(){
var title = document.getElementById("title").value
var description = document.getElementById("description").value
var start = document.getElementById("start").value
var end = document.getElementById("end").value
if(title!=""){
alert("Event Added Successfully")
this.props
.form({
variables: {
title:title,
description:description,
start:start,
end: end,
user_id: localStorage.getItem("id")
}
})
.then(({ data }) => {
alert("data is "+JSON.stringify(data.form))
if(data.form.message == "Event saved"){
this.props.saveUserEvents(data.form);
//alert("success")
//this.props.history.push('/Calender')
window.location.href="/Calendar";
return true;
}else {
alert("failure")
document.getElementById("messagegeneralNumber").innerHTML = '';
}
})
.catch(error => {
alert("data is "+JSON.stringify(error))
})
}
else{
alert("data not loading")
}
}
//connected to store-redux. using mapstatetoprops and mapdispatchtoprops
const mapStateToProps=(state, ownProps)=>{
return {
form: state
}
}
const mapDispatchToProps={
saveUserEvents
}
const Event = compose(
graphql(mutations.FORM,{name:'form'})
)(AddEvent);
export default withRouter(connect(mapStateToProps
,mapDispatchToProps)
(Event));
Your mapDispatchToProps should look like this:
const mapDispatchToProps = (dispatch) => {
saveUserEvents: (events)=>{
dispatch(saveUserEvents(events)); <--- here is your action creator that have to be dispatched
}
}

I use action to send a request and return the result to reducer, but store can't receive the result

when componentDidMount i dispatch an action to send a request, this is the action code below. receiveUserInfo just return data to reducer
export function requestUserInfo() {
return dispatch => {
dispatch(getUserInfo())
return axios.post('/user/getScores', qs.stringify({
token: token,
uid: uid
}))
.then(res => {
if (res.data.status !== 200) {
message.error(res.data.message)
return state
}
// return the data from server
return { ...res.data.attachment}
})
.then(data => {
// receiveUserInfo just return action and data
dispatch(receiveUserInfo(data))
})
}
}
this is the reducer code.
const token = localStorage.getItem('token')
const uid = localStorage.getItem('uid')
export const requestUserInfo = (state = {}, action) => {
switch (action.type) {
case 'HEADER_GET_USERINFO':
case 'HEADER_RECEIVE_USERINFO':
return Object.assign({}, state, action.data)
break
default:
return state
}
}
function receiveUserInfo(data) {
return {
type: 'HEADER_RECEIVE_USERINFO',
data
}
}

Correct reducers composition

How to get reducers not to return always new state? Is my solution ok or there are some better way?
My state shape:
userList: {
id: 'xxx', // <-|- Not mutable by reducers fields, so using combineReducers for each field is frustrating.
ba: 'xxx', // <-|
fo: 'xxx', // <-|
users: [
{
id: 'yyy',
be: 'yyy',
fo: 'yyy',
photo: {
id: 'zzz',
url: 'http://image.jpg', <-- The only field, that changes by reducers.
},
},
...
],
}
My current reducers:
// Always returns new object.
const userListReducer = (userList, action) => {
return {
...userList,
users: usersReducer(userList, action),
}
};
// Always returns new array.
const usersReducer = (users, action) => {
return users.map(user => userReducer(user, action));
};
// Always returns new object too.
const userReducer = (user, action) => {
return {
...user,
photo: photoReducer(user.photo, action),
};
};
// The only one true reducer.
const photoReducer = (photo, action) => {
switch (action.type) {
case 'update_photo':
return {
...photo,
url: action.url,
};
default:
return photo;
}
};
Solutions
1). Call usersReducer when it's necessary. Bad part: we need to care about logic other reducers.
const userListReducer = (userList, action) => {
switch (action.type) {
case 'update_photo':
return {
...userList,
users: usersReducer(userList, action),
};
default:
return userList;
}
};
2). Using combineReducers. Bad part: we need to care about all usersList's shape. Also, this still doesn't work, because usersReducer always returns new array.
const userListRecuer = combineReducers({
id: id => id,
ba: ba => ba,
fo: fo => fo,
users: usersReducer,
});
3). My solution. Using mergeOrReturnOld and mapOrReturnOld helpers.
const userListReducer = (userList, action) => {
return mergeOrReturnOld(userList, {
users: usersReducer(userList, action),
});
};
const usersReducer = (users, action) => {
return mapOrReturnOld(users, user => userReducer(user, action));
};
const userReducer = (user, action) => {
return mergeOrReturnOld(user, {
photo: photoReducer(user.photo, action),
});
};
helpers implementation:
const mergeOrReturnOld = (obj, addition) => {
let hasChanged = false;
for (let key in addition) {
if (addition.hasOwnProperty(key)) {
if (obj[key] !== addition[key]) {
hasChanged = true;
}
}
}
if (!hasChanged) {
return obj;
} else {
return {
...obj,
...addition,
};
}
};
const mapOrReturnOld = (array, callback) => {
let hasChanged = false;
const newArray = array.map(item => {
const newItem = callback(item);
if (newItem !== item) {
hasChanged = true;
}
return newItem;
});
if (!hasChanged) {
return array;
} else {
return newArray;
}
};

Resources