React array state update - reactjs

I am getting a book list from database and is stored in a state variable Book list also has book price field
const [books, setBooks]=useState([])
setBooks(data)
Each books object in the array has properties like BookName, Author , Price, Discount
I have rendered a html table like follows
return ( <div>
{books.map((x,i) => ( <tr>
<td>x.bookName</td>
<td>x.price</td>
<td><MyCustomTextInput onChange={e => handleChange(e, x.id)} value={x.discount}></MyCustomTextInput></td>
<tr></div>);
the sample code for MyCustomTextInput is as follows
function MyCustomTextInput(props)
{ return (<div><TextInput></TextInput> </div>)
} exports default MyCustomTextInput
The code where I update the price for corresponding object in "books" array is as follows
function handleChange(x,id){
var obj = books[id];
obj.price = obj.price - e.target.value; //I am correctly getting discount in e.target.value
}
Every thing works properly except the price after discount is not reflecting on the UI. though its getting updated properly in the array but not reflecting on the UI.
any help....
Experts -

This is setting a value:
function handleChange(x, id){
var obj = books[id];
obj.price = obj.price - e.target.value;
}
But it's not updating state. The main rule to follow here is to never mutate state directly. The result of bending that rule here is that this update never tells React to re-render the component. And any re-render triggered anywhere else is going to clobber the value you updated here since state was never updated.
You need to call setBooks to update the state. For example:
function handleChange(x, id){
setBooks(books.map(b =>
b.id === id ? { ...b, price: b.price - parseFloat(e.target.value) } : b
));
}
What's essentially happening here is that you take the existing array in state and use .map() to project it to a new array. In that projection you look for the record with the matching id and create the new version of that record, all other records are projected as-is. This new array (the result of .map()) is then set as the new updated state.
There are of course other ways to construct the new array. You could grab the array element and update it the way you already are and then combine it with a spread of the result of a .filter to build the new array. Any construction which makes sense to you would work fine. The main point is that you need to construct a new array and set that as the new state value.
This will trigger React to re-render the component and the newly updated state will be reflected in the render.

You need to setBooks to update state books.
function handleChange(x, id) {
setBooks(
books.map((item) =>
item.id === id ? { ...item, price: item.price - parseFloat(e.target.value) } : item,
),
);
}

To achieve that, you need to call setBooks after changing the price within handleChange method to re-render the component with the newly updated state.
It's simply like the following:
function handleChange(x,id){
var obj = books[id];
obj.price = obj.price - e.target.value; //I am correctly getting discount in e.target.value
setBooks([...books]);
}

Related

How to keep the check box checked , when navigating to other page of react pagination

I want to ask , how to keep save the id's of the check boxes in a state , and whenever i switched back to first page it automatically search the element with id and mark check boxes automatically.
and if i unmark the checkbox , it deletes the id from the state.
i am able to think about the logic , but cant able to code,it
Small help ,will leads to solve this problem
While switching to other pages, i am succesfully saving the data ,by updating the state
`
// push all the unique objects (combination of previous state of selectedPayments and data from list)
setSelectedPayments((prevState) => {
var arr = [...prevState, ...list];
var newState = [
...new Map(arr.map((item) => [item.id, item])).values(),
];
return newState;
});
console.log('Selected payments are', selectedPayments);
`
Also , removing the objects , if again the checkbox is unchecked ,and updating the state
`
// pull all the objects , which got unChecked
setSelectedPayments((prevState) => {
var newState = prevState.filter(function (objFromA) {
return !list.find(function (objFromB) {
return objFromA.id === objFromB.id;
});
});
return newState;
});
`
Only facing issue with keeping track of the checked boxes, i have implimented this, this is keeping track of main(parent checkbox).
How to extract the ids saved and check the checkboxes when we naviagete from one page to another
`
let elementId = e.target.id;
if (selectedBoxes.includes(elementId)) {
const newArray = selectedBoxes.filter((e) => e !== elementId);
setSelectedBoxes(newArray);
} else {
setSelectedBoxes((prevState) => {
return [...prevState, elementId];
});
}
`
First i modified the Res Json , so that it set's a property isSelected = true,
by comparing the element from the selectedPayments
inAll check handler , i set the selectedPayments like this
And render using this
This is how ,i solved this problem.
** Better and improved answers are always welcome, please share your views.

Why am I mutating the original array with filter method? [React]

After executing the newData[0].id = newValue I am actually updating the react initialData state. How is that possible?
Is my understanding that filter should return a new array different than the original one, also I am not ussing the setState feature so I don't understand why the state is changing.
Because arrays are mutable. it will keep the reference to the original array even after filtering.
use the spread operator to avoid mutating the original array
const data = [...newData]
data[0].id = newValue
As per the new beta docs on updating items in array
setInitialData(prev => {
// create a new array
const withReplaced = prev.map(elem => {
if (elem.id === id) {
const newVal = //set new value
// create a new updated element
return {
...elem,
id: newVal
}
} else {
// The rest haven't changed
return elem;
}
});
return withReplaced;
})
Hope it helps
you can't update the initialData,but the you can update the son of the array.And if you don't use "setData".The views won't change.

React - problem updating an array of objects using hooks

I have tried a bunch of approaches but on this, but it's still bugging the hell out of me. I am using React 17.0.1 in this project.
I have a array of objects formatted like so:
gameNumberFields: [{ name: 'gameNum', placeholder: 'GGG', size: '3', value: '', dataindex: '0' }];
For now, there is just one object in the array, but there is always the possibility of more down the road (hence why it's an array).
In the code - this field is pre-populated on initialization - so the "value" of the first index in the array might be something like "123". I use initialState to make this happen:
const [gameNumberFields, setGameNumberFields] = useState(scratchTicketFields?.gameNumberFields ?? []);
When the display is shown to the user - this value is shown to the user in an field using the defaultValue.
return gameNumberFields.map((field, index) => {
const ref = React.createRef();
elemRefs.push(ref);
return (
<div className='d-inline-block ticket-number-inputs' key={`game-numbers--${index}`}>
<input
type='text'
id={field.name}
data-index={field.dataindex}
ref={ref}
className='theme-input'
placeholder={field.placeholder}
size={field.size}
maxLength={field.size}
defaultValue={field.value}
onBlur={(event) => handleGameNumberChange(event, field)}
onKeyUp={(event) => autoTab(event, elemRefs)}
required
/>
<span className='dash'>—</span>
</div>
);
});
}; // end GameIdFieldInputs
So far - so good. The problem I am having is in the onBlur event handler. For some reason - when the user changes the value to something else - it always goes back to the old value.
Here is the handler:
const handleGameNumberChange = async (event, field) => {
// get gameNum from the event target
const gameNum = event.target.value; // say this becomes 999
// do a deep copy of the gameNumberField state.
let gameIdField = JSON.parse(JSON.stringify(gameNumberFields));
// check that we are changing the right index in the array
const fieldIndex = gameIdField.findIndex((obj) => obj.name == field.name);
// make a new object changing the value to 999
let newGameObject = { ...gameIdField[fieldIndex], value: gameNum };
console.log('newGameObject', newGameObject);
//NOTE: At this point the newGameObject is correct and updated with the NEW gameNum of 999
// create a new array and PUSH the new game object onto it
let newGameIdArray = new Array();
newGameIdArray.push(newGameObject);
// Once pushed the array has the OLD game number in it . . . so 123 - WHY?!?!
console.log('newGameObjectArray', newGameIdArray);
setGameNumberFields(newGameIdArray); // updates with the 123 game number. . .
}; // end handleGameNumberChange
So in the method, I deep copy the gameNumberFields into a mutable object. I then update the object with the new gameNumber (from 123 to 999) and all works when I print it out with my console.log for newGameObject.
As soon as I push this object in the new Array - it changes back to 123! Can anyone see a flaw in my code here?
When I finally do call setGameNumberFields - it does set the state (I have a useEffect that prints out the values) but again, its always the OLD values.
Any help is welcome and appreciated!

react set state - unable to set key name with dot or bracket notation

I am needing to setup state for a dynamic set of data. After the data loads, in the ComponentDidUpdate function I'm iterating through the dataset and then trying to setState for each item in state based on the items Id. I'm choosing id over index because the index isn't accessible once data is displayed and I won't be able to reference it.
When trying to set state I get an error in the IDE that the dot notation or the bracket notation is unexpected.
componentDidUpdate(prevProps, prevState){
if(this.props.report !== prevProps.report && this.props.report !== undefined){
//set checkbox state dynamically for each row
this.props.report.systemSourceList.forEach((item) =>
this.setState((prevState) => ({selectedSourceRowIds : [...prevState.selectedSourceRowIds, {item['sourceId']: true}]}))
}
}
I've tried both item.sourceId and item['sourceId'] but neither work.
I also assigned item.sourceId to a variable and logged it to the console succesfully, but when I plug that variable into the keyName space the id of the source isn't used just id.
this.props.report.systemSourceList.forEach((item) => {
let id = item.sourceId;
console.log(id);
this.setState((prevState) => ({selectedSourceRowIds : [...prevState.selectedSourceRowIds, {id: true}]}))
});
}
The objects being looped through look like:
{
systemId: 13,
facilityId: 4747,
sourceId: "WS010",
}
What am I doing wrong? Is there another way to go about this?
UPDATE
I got it - I needed to place brackets around the actual keyName. Final solution:
this.props.report.systemSourceList.forEach((item) => {
let id = item.sourceId;
console.log(id);
this.setState((prevState) => ({selectedSourceRowIds : [...prevState.selectedSourceRowIds, {[id]: true}]}))
});

How to update object in vuex store array?

I'm pretty new to vue/vuex/vuetify but starting to get the hang of it.
I have a problem though I haven't been able to solve properly.
I have an array of "projects" in my store. When deleting and adding items to the store via mutations the changes reflect properly in subcomponents referencing the array as a property.
However, changes to items in the array does not reflect even though I can see that the array in the store is updated.
The only way I got it to "work" with an update action was to either :
remove the project from the array in the store and then add it
use code that sort of does exactly the same as described above but like so:
state.categories = [
...state.categories.filter(element => element.id !== id),
category
]
But the problem with the above two methods is that the order of the array gets changed and I would really like to avoid that..
So basically, how would I rewrite my mutation method below to make the state reflect to subcomponents and keep the order of the array?
updateProject(state, project) {
var index = state.projects.findIndex(function (item, i) {
return item.id === project.id;
});
state.projects[index] = project;
}
You can use slice to inject edited project in correct position:
updateProject(state, project) {
var index = state.projects.findIndex(function(item, i) {
return item.id === project.id;
});
state.projects = [
...state.projects.slice(0, index),
project,
...state.projects.slice(index + 1)
]
}
or you can use JSON.parse(JSON.stringify()) to make a deep copy of object
updateProject(state, project) {
var index = state.projects.findIndex(function(item, i) {
return item.id === project.id;
});
state.projects[index] = project;
state.projects = JSON.parse(JSON.stringify(state.projects))
}

Resources