import React, { useState } from "react";
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState({
name: "pankaj",
age: 12,
city: "alwar"
});
const [data, updateData] = useState([
{ id: 1, name: "Pankaj 1" },
{ id: 2, name: "Pankaj 2" },
{ id: 3, name: "Pankaj 3" },
{ id: 4, name: "Pankaj 4" }
]);
const updateStateData = id => {
let item = data.find(item => item.id == id);
item.name += " updated 1";
updateData(data);
};
return (
<div>
<p>You clicked {count.name} name</p>
<p>You clicked {count.age} age</p>
<p>You clicked {count.city} city</p>
<button
onClick={() =>
setCount({
name: count.name + " updated",
age: count.age,
city: count.city
})
}
>
Update Name
</button>
<button
onClick={() =>
setCount({ name: count.name, age: count.age + 10, city: count.city })
}
>
Update Age
</button>
<button
onClick={() =>
setCount({
name: count.name,
age: count.age,
city: count.city + " updated"
})
}
>
Update City
</button>
<div>
{data.map(item => {
console.log({ item });
return (
<div>
<p>{item.id}</p>
<p>{item.name}</p>
<button onClick={() => updateStateData(item.id)}>Update</button>
</div>
);
})}
</div>
</div>
);
}
export default Example;
I create a functional component and in this component, I am using the useState hook, the second useState([data,updateData]) hook contains an array of objects and I am displaying all items in the component. With each rendered item there is button and onClick of this button I take the id of item and update the name of that particular item and assign newly updated array to "updateData" method that will update data of useState hook.
But now the issue is that array is going update(data) but changes are not reflecting in view.
If I make the below replace the code of "updateStateData" method with below code then its work fine
const updateStateData = (id) => {
let item = data.find(item => item.id == id);
item.name += ' updated 1';
updateData([...data]);
}
So, why I need to assign a new array([...data]) to reflect the changes.
The state updater returned by useState
will not rerender the component's children if you set a new value that equals the current value. https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update
Why this behavior is different from Class component.
import React, { useState } from "react";
export default class StateFullComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{ id: 1, name: "Pankaj 1" },
{ id: 2, name: "Pankaj 2" },
{ id: 3, name: "Pankaj 3" },
{ id: 4, name: "Pankaj 4" }
]
};
}
updateStateData = id => {
let item = this.state.data.find(item => item.id == id);
item.name += " updated";
console.log(this.state.data);
this.setState({
data: this.state.data
});
};
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<div>
{this.state.data.map(item => (
<div key={item.id}>
<p>{item.id}</p>
<p>{item.name}</p>
<button onClick={() => this.updateStateData(item.id)}>
Update
</button>
</div>
))}
</div>
</div>
);
}
}
This Class component code is similar to our functional hook component. But in this class component changes are rerender but not in functional hook component. Does react uses the Object.is comparison algorithm only in case of Hooks?
I'm not sure on the exact reasoning, but yes, class components behave differently. Class components will rerender even if the next state equals the previous state. Also, the state of a class component must be an object and supports shallow merging during updates. Contrastingly, useState
does not require the state to be an object and will not shallowly merge the next state onto the previous state while updating.
exactly as @malerba118 said, state hooks will not rerender if you pass the same value/object to it. React encourages an immutable approach to ensure consistency, especially between renders. I'd go further and rewrite your handler to -
function updateStateData(id){
return data.map(item => {
if(item.id !== id) return item
return {...item, name: item.name + ' updated'}
})
}
Hope this helped! This isn't a bug or a feature request, so I'm closing this issue.
Most helpful comment
exactly as @malerba118 said, state hooks will not rerender if you pass the same value/object to it. React encourages an immutable approach to ensure consistency, especially between renders. I'd go further and rewrite your handler to -
Hope this helped! This isn't a bug or a feature request, so I'm closing this issue.