React Reducer - Add elements to existing element - reactjs

i want that new updates get continuous added to "kanbanboard" but instead the old value get signed over. enter image description here
ACTION-Function
export const editExpense = (id, updates) => ({
type: "EDIT_EXPENSE",
id,
updates
});
REDUCER-FUNCTION
case "EDIT_EXPENSE":
return state.map((expense) => {
if (expense.id === action.id) {
return {
...expense,
kanbanboard:[{...action.updates}]
};
} else {
return expense;
};
});
Thank you for helping.

I cant see the entire reducer but it looks like you are explicitly overriding the old value. Change it to:
case "EDIT_EXPENSE":
return state.map((expense) => {
if (expense.id === action.id) {
return {
...expense,
kanbanboard:[
...expense.kanbanboard, // you have to add your old state here
{
...action.updates
}]
};
} else {
return expense;
};
});

Related

Update the Old and New Value in Redux

I have a problem updating the old value of the array in my redux react app. I have successfully updated the new selected object true. I want to other object to set to false since I have set the another object to true.
const initialState = {
annualPlans: [],
};
const planReducer = (state = initialState, action) => {
switch (action.type) {
case planConstants.UPGRADE_PLAN_SUCCESS:
return {
...state,
annualPlans: state.annualPlans.map((todo) =>
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return original todo
todo
),
};
default:
return state;
}
};
It seems like you want to return current set to false for the others:
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return with current false
{ ...todo, current: false }
Id first create the new todos by looping though a map and then assign to the state
case planConstants.UPGRADE_PLAN_SUCCESS: {
const newTodos = state.annualPlans.map((todo) => {
if (todo.value === action.data.plan_id) {
return { ...todo, current: true }; // if todo.id matched then set the current to true and return;
}
if (todo.current) {
return { ...todo, current: false }; // else if current is already true, then false it and return
}
return todo; // else return original todo
});
return {
...state,
annualPlans: newTodos
};
}
.....
This will optimize the rendering and prevent of looping multiple times.
No need to re create all todo items, if you use pure components then that will mess up this optimisation. You can just add a map to deal with resetting the current value:
const planReducer = (state = initialState, action) => {
switch (action.type) {
case planConstants.UPGRADE_PLAN_SUCCESS:
return {
...state,
annualPlans: state.annualPlans
.map(
(todo) =>
todo.current
? { ...todo, current: false } //reset current to false
: todo // no need to change this one
)
.map((todo) =>
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return original todo
todo
),
};
default:
return state;
}
};

React native push with multiple key and value

I have a group of checkboxes, whenever I select a checkbox I need to push an array of data, like { 'index':1, 'photo':'sample.png' } to state, and whenever I unselecting the checkbox, I need to remove it from the state. after I need to loop through the state to get index and photo to be used
handleSelection = async (media, index, isSelected) => {
alert(index);
if (isSelected == true) {
this.state.mediaSelected.push(media.photo);
} else {
this.state.mediaSelected.splice(this.state.mediaSelected.indexOf(media.photo), 1);
}
console.warn(this.state.mediaSelected);
}
this is working for single value without the key, is there any way to push it with key and value?
You should always update state with this.setState in your case would be something like this:
handleSelection = async (media, index, isSelected) => {
alert(index);
if (isSelected == true) {
this.setState({
mediaSelected: this.state.mediaSelected.push({
index,
photo: media.photo
})
});
} else {
this.setState({
mediaSelected: this.state.mediaSelected.splice(this.state.mediaSelected.indexOf(media.photo), 1)
});
}
console.warn(this.state.mediaSelected);
}
Try this:
Sorry I am working as well as answering your question so it is taking time.
handleSelection = async (media, index, isSelected) => {
let selectPhotosObj = this.state.selectPhotosObj || [];
if (isSelected == true) {
const data = { index, photo: media.photo };
//this.state.selectedPhotoObj will be the container for your object.
selectPhotosObj.push(data)
//need to set the new Array of Object to the state.
this.setState({ mediaSelected: media.photo, selectPhotosObj });
} else {
const removedPhoto = this.state.mediaSelected.filter(value => value !== media.photo);
selectPhotosObj = this.state.selectedPhotosObj.filter(value => value.index !== index);
this.setState({
mediaSelected: removedPhoto,
selectPhotosObj
})
}
console.warn(selectPhotosObj);
}

how to correctly pass array to state

I have problem with react setState i want to pass to state disciplinesArray
in function setArray(). sorting goes well but array is not filtred. in console log in setArray() I see filtered and sort array but state is only sort.
All works until I add setToggle() and setInitialArray()
constructor(props) {
super(props)
this.state = {
filterType: 'all',
sortType: 'alphabetical',
arrayOfDisciplines: this.setInitialArray()
}
}
componentDidUpdate(prevState) {
console.log(this.state)
if (prevState===this.state) {
this.setState({
disciplinesArray: this.setArray()
})
}
}
setInitialArray() {
let disciplinesArray = this.props.disciplines
disciplinesArray = disciplinesArray.map((discipline) => {
return discipline = {
...discipline,
score: disciplineScore(this.props.athlete.skillset, discipline.requirements),
isHidden: false
}
})
return disciplinesArray
}
setArray() {
let disciplinesArray = this.state.arrayOfDisciplines
switch (this.state.sortType) {
case 'alphabetical':
disciplinesArray = disciplinesArray.sort((a, b) => {
return a.name.localeCompare(b.name)
})
break
case 'score':
disciplinesArray = disciplinesArray.sort((a, b) => {
return a.score - b.score
})
break
default:
}
switch (this.state.filterType) {
case 'team':
console.log('team')
disciplinesArray = disciplinesArray.filter((discipline) => {
return discipline.isIndividual === false
})
break
case 'individual':
console.log('team')
disciplinesArray = disciplinesArray.filter((discipline) => {
return discipline.isIndividual === true
})
break
default:
}
return disciplinesArray
}
setToggle(disciplineName, props) {
let disciplinesArray = this.state.arrayOfDisciplined
disciplinesArray.find((discipline) => {
return discipline.name === disciplineName
}).isHidden = !disciplinesArray.find((discipline) => {
return discipline.name === disciplineName
}).isHidden
console.log(disciplineArray)
return disciplinesArray
}
setSortType(e) {
this.setState({
sortType: e.target.value
})
}
setFilterType(e) {
this.setState({
filterType: e.target.value
})
}
}
I'm expecting filtered and sorted array in state
I am afraid that your code is full of small bugs. The fact that sorting works is actually a random consequence of another bug. Consider that your initial state:
this.state = {
filterType: 'all',
sortType: 'alphabetical',
arrayOfDisciplines: this.setInitialArray()
}
Stores the initial array into this.state.arrayOfDisciplines.
When changing the value however:
this.setState({
disciplinesArray: this.setArray()
})
You are storing that to this.state.disciplinesArray.
That cannot work, they should be the same.
The fact that it works is caused by another bug:
let disciplinesArray = this.state.arrayOfDisciplines
switch (this.state.sortType) {
case 'alphabetical':
disciplinesArray = disciplinesArray.sort((a, b) => {
return a.name.localeCompare(b.name)
})
break
This actually takes the reference to the array in your state, sorts that array in your state and stores in a local variable. Note that Array.sort sorts an array in-place, therefore you should usually prefix that by slice:
disciplinesArray = disciplinesArray.slice().sort((a, b) => {
return a.name.localeCompare(b.name)
})
In summary: your bug is a bad property name in setState and the fact that you are changing the value inside state directly.
The same problem can be seen also in setToggle function, which also modifies state directly. It should look something like this:
return this.state.arrayOfDisciplined
// always create a new array, don't just update a value inside the array
.map(discipline => {
if (discipline.name !== disciplineName) {
return discipline;
}
// always create a new immutable object, don't mutate the old state
return {
...discipline,
isHidden: !discipline.isHidden
};
});

React Redux: Update and replace records with another records returns value of 1

React Redux: Update and replace records with another records returns value of 1.
On the server side, I have a json response [{"id":"10", "food_name":"Rice"}]
The code below works fine by displaying a food item called Rice from the database via API Call as showed in the json array above.
Now I have a requirements to replace the displayed food item Rice with Beans.
To this effect, I have a json files which is to be returned via API Call after posting
[{"id":"10", "food_name":"Beans"}]
I have also created a Post button which should send data to the server side and return the response Beans.
Here is my effort as well as my Issue which is caused by reducer.
If Implement the code below in the reducer
case foodConstants.FOOD_SUCCESS_POST:
return {
items: state.items.map(food1 => {
if (food1.id === action.id) {
//return { ...food1, food_name: state.items[0].food_name};
return { ...food1, food_name: 'Beans' };
}
The Code works fine and Rice is replaced with Beans since I set value beans in the reducer.
but since I need to get the records via API Call so if implement
case foodConstants.FOOD_SUCCESS_POST:
return {
items: state.items.map(food1 => {
if (food1.id === action.id) {
return { ...food1, food_name: state.items[0].food_name};
}
Am getting value of 1 replacing Rice instead of Beans. Please where is this value of 1 coming from.
I need to have beans replace record Rice as a value returned from API Call.
My action and service code are okay as I can see the json returned records in the array as per
[{"id":"10", "food_name":"Beans"}]
I think my problem lies in this line of code below which returns value of 1 instaed of Beans.
return { ...food1, food_name: state.items[0].food_name};
Here is the full code
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { foodActions } from 'actions';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(foodActions.getFood());
}
handleFood(id,food_type) {
return (e) => this.props.dispatch(foodActions.postfood(food_id));
}
render() {
const { food1, foods1 } = this.props;
return (
<div>
{foods1.items &&
<ul>
{foods1.items.map((food1, index1) =>
<li key={food1.id}>
{food1.food_name}
<input type="button" value="Post and Update Food Name" onClick={this.handleFood(food1.id)} />
</li>
)}
</ul>
}
</div>
);
}
}
function mapStateToProps(state) {
const { foods1} = state;
const { food1 } = state;
return {
food1, foods1
};
}
const connectedApp = connect(mapStateToProps)(App);
export { connectedApp as App };
Reducer Code
import { foodConstants } from '/constants';
export function foods1(state = {}, action) {
switch (action.type) {
case foodConstants.GETALL_REQUEST:
return {loading: true};
case foodConstants.GETALL_SUCCESS:
return {
loading: false,
error: null,
items: action.foods1,
};
case foodConstants.GETALL_FAILURE:
return {
error: action.error
};
// Post and Update Food Name
case foodConstants.FOOD_REQUEST_POST:
return {...state};
case foodConstants.FOOD_SUCCESS_POST:
return {
items: state.items.map(food1 => {
if (food1.id === action.id) {
return { ...food1, food_name: state.items[0].food_name};
}
return food1;
})
};
case foodConstants.FOOD_FAILURE_POST:
return {
error: action.error
};
default:
return state
}
}
You need to replace value that is coming in action, but you are picking from state
case foodConstants.FOOD_SUCCESS_POST: {
const updatedItems = state.items.map((food1) => {
if (food1.id === action.id) {
return { ...action };
}
return food1;
});
return { ...state, items: updatedItems };
}
Or you can do this as well
case foodConstants.FOOD_SUCCESS_POST: {
let updatedItems = { ...state.items };
const itemIndex = updatedItems.findIndex((food1) => food1.id === action.id);
if(itemIndex > -1){
updatedItems[itemIndex] = {
...updatedItems[itemIndex],
...action,
}
}
return { ...state, items: updatedItems };
}

React/Redux updating a certain value in an array of objects

I am just learning redux and this is my first time using it in a project. I am trying to update a certain value in an array of objects. The structure of my object is:
students: {
loading: false,
error: null,
data: [{
id: 1,
name: "Bob",
email: 'whatever#gmail.com',
status: 'out'
}]
}
Below are my actions for this and the data it gets back is the id of the student that it needs to update. These work fine.
export const studentCheckInStart = student => ({
type: "STUDENT_CHECK_IN_START",
student
})
export const studentCheckIn = (id) => {
return dispatch => {
dispatch(studentCheckInStart())
return axios.put('http://localhost:8080/studentList/'+id)
.then((response) => {
dispatch(studentCheckInSuccess(response.data))
}).catch(err => {
dispatch(studentCheckInError(err))
})
}
}
export const studentCheckInSuccess = (data) => {
return {
type: STUDENT_CHECK_IN_SUCCESS,
payload: data
}
}
export const studentCheckInError = (error) => {
return {
type: STUDENT_CHECK_IN_ERROR,
error
}
}
Where I'm having the issue is in the reducer
case "STUDENT_CHECK_IN_SUCCESS":
let updatedStudent = state.students.data.findIndex((student) => {
return student.id === action.payload
})
console.log(updatedStudent)
return {
...state,
students: {
...state.students[updatedStudent],
data: {
status:'in'
}
}
};
break;
case "STUDENT_CHECK_IN_START":
return {
...state,
students: {
...state.students,
loading: true
}
}
break;
case "STUDENT_CHECK_IN_ERROR":
return {
...state,
students: {
...state.students,
error: action.payload,
loading: false
}
}
break;
I'm trying to target the specific student object using the id to find the index of the student I want to target. Then change just the status of that object to "in". I know what I have in the STUDENT_CHECK_IN_SUCCESS is incorrect, I'm just not sure how to do it.
Your state seems a little bit complex. Why do you need loading or error in your students object? What other parts do you have in your state beside students? This is one possible way I can think of at this situation (just the related part) :
let updatedStudent = state.students.data.findIndex(
student => student.id === action.payload
);
const newData = [ ...state.students.data ];
newData[ updatedStudent ] = { ...newData[ updatedStudent ], status: "in" }
return { ...state, students: { ...state.students, data: newData } };
I will edit my answer if I think a better way.
It looks like your action doesn't really need all that payload, just an id of the student who checked in. So if you change that, I think you could return this from your reducer action:
return {
...state,
students: {
...state.students,
data: state.students.data.map(s => {
if (s.id === action.id) {
return { ...s, status: 'in' };
}
return s;
}
}
};
The idea is that you need to return everything unchanged except the data array. By using map, we can return a modified version of the data array where the student whose id matches the one supplied in the action will have their status changed to in, but the rest of the students in the data array remain unchanged.

Resources