Adding an element to an existing array in react hooks - reactjs

So I have this state variable:
const [Class, setClass] = useState({ Teacher: "John Fartsalot", Year: 2021, Students: [] });
and I have a:
ver studentObject
I want to push the studentObject into the Students array dynamically using setClass.
I came across this post: Push method in React Hooks (useState)? but it seems to be relevant only when there's just the list in there.
What is the correct way to do so in my case?

While you could spread the existing Class into a new object, and then spread the new students array into that new object as well, it would be better to follow the convention and separate out the state variables (as React recommends), and to not use Class as a variable name (it's very close to the reserved word class). Consider:
const [teacher, setTeacher] = useState('John Fartsalot');
const [year, setYear] = useState(2021);
const [students, setStudents] = useState([]);
Then to add to the students array, do
setStudents([...students, studentObject]);
Or, if students happens to be in a stale closure at this point, then
setStudents(students => [...students, studentObject]);

This is correct way to add:
setStudents(students => [...students, studentObject]);

Related

How to add and update json array elements using react hooks

I defined a state with null array, Then that array will contain firstname, lastname etc..based on entry in text field. I need to add the prop if not present and update the prop if present. I need to do validation in save based on that. I tried with below code, but that not works.
const [signerData, setSignerData] = useState([]);
const newItems = [...signerData];
newItems["firstName"] = inputValue;
setSignerData({ newItems });
Any help is appreciated. Thank you.
Firstly, you would need an object instead of an array. Array doesn't allow you to have keys, only values. The below code would work.
const [signerData, setSignerData] = useState({});
setSignerData({ ...signerData, firstName: inputValue });

how can i get the information inside react js object

how can I access the name inside the object and put it inside a variable
const newItem = [...items].filter((item) => id === item.id);
const comments = newItem.price;
console.log([newItem.price]);
I tried this one but didn't work, console says undefined
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
filter() method
You can get price by usign the below way.
const newItem = [...items].filter((item) => id===item.id);
const comments= newItem[0].price;
console.log(newItem[0].price)

How to mutate an object inside array in react.js state?

I have an issue when i trying to updating the state.
here is my code:
const [todos, setTodos]= useState([])
useEffect(() => {
setTodos([
{id:1, title: '',
notes: [{id: 1, description: 'this is a simple description'}]}
])
}, [])
my goal is to add a note to the array of todos.
i try like this
const i = todos.findIndex((t) => t.id === parseInt(id));
const newArr = todos[i].notes[0].push(note);
setTasks(newArr);
but it's not working the newArr gives me the index note the new state.
Please help
Thanks in advance
if you want to get it working you can do something like below:
const newArr = todos[i].notes.push(note)
but it's not the recommended way.
The best way to add new item in to your notes array is to use object.assign or spread operator in order to not directly mutate your entire array. some thing like below:
const newArr = [...todos[i], note]
and also use this way of mutating your entire tasks array.
I think it's been well-described ar here if you want to get more information around why you should use spread operator instead of push.
I have created this which works perfectly. What you have to do is to obtain the todo id and push to its note. Here the todo state gets updated but render method is not called. In order to call the render method I have called setTodo again.

Why is useState hook changing another object without calling it?

I have a React component that retrieves data when it first loads.
const [data, setDate] = useState(null);
const [originalData, setOriginalData] = useState(null);
useEffect(() => {
async function fn() {
let res = await getObject();
setData({...res});
setOriginalData({...res});
}
fn();
}, [])
I call 2 different hooks to set the data and originalData states.
The purpose of this is so that I have an unchanged version of data that I can refer to for some of the logic in the component.
However when I make a change to the data state it seems to also be changing the originalData state as well without me calling anything.
const change() => {
let updatedData = {...data};
updatedData.someProperty = 'newValue';
setData(updatedData);
}
I would have expected that data now contains the property someProperty with the value newValue, and that originalData will be unchanged from the initial load.
But when I compare them, data and originalData both now have someProperty.
Can anyone point me in the right direction?
EDIT: add spread operator
Problem: Same Reference
The reason why changing data changes originalData is not because of React hooks, but because of the way objects behave in JavaScript. Though data and originalData are different, they are still referring to the same object (from res). So any changes made to either one of these will be reflected on the other, since they have same reference.
Solution: Clone
For this reason you should clone the data instead of directly assigning them.
// JSON
const originalData = JSON.parse(JSON.stringify(res))
// Object.assign
const originalData = Object.assign({}, res)
// Or spread opr
const originalData = { ...res }
// Then set the state
setOriginalData(originalData);
ES6 methods like Object.assign and spread operator does a shallow copy. If you need a deep copy and if your data doesn't have Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, go for JSON parse, stringify.
Check out this question for more details about cloning in JavaScript

Redux key based array not triggering new props when added to 2nd time

I am dealing with Proposals and locations.
A location can have multiple proposals.
The component is passed a location object (below as passedProps) to show all the proposals for this location :
<Proposals location={ location } key={location.id}/>
Here is are my redux props :
const mapStateToProps = (state , passedProps) => {
return {
proposals : state.propertyProposals[passedProps.location.id]
};
};
when adding a new proposal, I want to store their ids by location, so in a addition to storing the proposal, I am storing their Ids, in an array keyed by location Id.
The first proposal added and refreshed with new props just fine, however even though the second one is successfully pushed to the array, this is not triggering new props, so it does not "refresh" -- If I leave the route and come back I see the new 2nd proposal (which did not show the first time)
Here is the PROPOSAL_CREATE action for a new Proposal.
type :'PROPOSAL_CREATE',
payload :
{'e7ef104e-19ed-acc8-7db5-8f13839faae3' : {
id : 'e7ef104e-19ed-acc8-7db5-8f13839faae3',
locationId: '41e9c5d8-a520-7e3b-939a-12f784d49712'
}
}
here is the case which handles it :
case 'PROPOSAL_CREATE':
{
const proposal = Object.values(action.payload)[0];
const locationId = proposal.locationId;
let newState = {...state}
if (locationId in newState) {
newState[locationId].push(proposal.id)
} else {
newState[locationId] = [proposal.id]
}
return newState
}
Is there an obvious reason I am not seeing the change in the component for the second entry?
Thanks
There is one issue here. Your store state is not immutable. You have used below line to make a copy:
let newState = {...state}
Here it does make copy of object but it's shallow copy, hence your array object in newState and state have the same reference. That's why redux doesn't identify the change in store and hence props are not updated in sequence.
You can clone your state by below methods:
let newState = JSON.parse(JSON.stringify(state));
OR if you use jQuery then:
let newState = $.extend(true, {}, state);
I think this will surely fix your issue.
Based on your reducer logic i think that you did not specify action type.
The one of the redux conventions is the action recognition based on type property.
I bet that you forgot to specify that property.
var properAction = {
type: 'PROPOSAL_CREATE',
payload: {
{
'e7ef104e-19ed-acc8-7db5-8f13839faae3': {
id: 'e7ef104e-19ed-acc8-7db5-8f13839faae3',
locationId: '41e9c5d8-a520-7e3b-939a-12f784d49712'
}
}
}
I would recommend you to write action creators it will reduce your place for typos like that.
Cheers!
2 things:
I forgot that Arrays of an original object are still by reference. So even after
let newState = {...state}
newState[locationId]
has the same reference as
state[locationId]
As a result my original statement was mutating the original state, not creating a newState
and so
newState[locationId].push(proposal.id)
needed to be
newState[locationId] = state[locationId].concat(proposal.id);
or es6
newState[locationId] = [ ...state[locationId] , proposal.id ] ;

Resources