V R
V R

Reputation: 143

React array state update

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 -

Upvotes: 0

Views: 4604

Answers (3)

Amer
Amer

Reputation: 6716

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]);
}

Upvotes: 1

David
David

Reputation: 219057

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.

Upvotes: 2

Viet
Viet

Reputation: 12807

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,
    ),
  );
}

Upvotes: 3

Related Questions