Reputation: 53
Code, i am trying to copy the list as another list and modifying the name property, I was wondering why would this not work, i know that shallow copying this works/ also when i use a fn updater it works , thanks
import { useState } from "react";
const initList = { name: "abhi" };
export default function List() {
const [list, setList] = useState(initList);
function handleClick() {
const nextList = list;
nextList.name = "anand";
// setList((list) => ({ ...list }));
setList(nextList);
}
return (
<>
<button onClick={handleClick}>change name</button>
<ul>{list.name}</ul>
</>
);
}
Edit: I understand the what and how, wanted to know why this happens in Reactjs. I have got my answer, thank you so much guys for helping me out
Upvotes: 1
Views: 102
Reputation: 2197
You've got answers already but to be precise here's the piece of source code from react repo that I believe is responsible for what you're seeing:
// Mark that the fiber performed work, but only if the new state is
// different from the current state.
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
You can find the definition of is
function here. The important bit is that it compares two passed objects (old state and a new state) by reference. Since you did not make a shallow copy of the new state these are in fact the same and no update is made.
Perhaps an interesting thing is that using functional version of setState does not automatically save you from the same mistake. It also has to return a different reference from the old state. In other words, this also won't update state:
const nextList = list;
nextList.name = "anand";
setList((list) => nextList);
Upvotes: 3
Reputation: 1
I didn't quite understand your question, but you would like to keep the object's initial value { name: "abhi" }
and on const nextList
change name to "anand", right? getting like this: initList = { name: "abhi" }
and list = { name: "anand" }
? If so, just do this:
import { useState } from "react";
const initList = { name: "Test 1" };
export default function List() {
const [list, setList] = useState({ ...initList });
function handleClick() {
const nextList = list;
nextList.name = "Test 2";
// setList((list) => ({ ...list }));
setList(nextList);
}
return (
<>
<button onClick={handleClick}>change name</button>
<ul>{list.name}</ul>
<ul>{initList.name}</ul>
</>
);
}
This happens because you are inserting the object and its pointer to the state of the list
, in this case what you need is just the initial values of the object and not the complete one.
Upvotes: 0
Reputation: 7520
const nextList = list
will not create a new object, it will create a variable nextList
that points to the same object as list
.
Consequently, when you mutate the object referenced by nextList
, you're mutating the same object referenced by list
.
Read more here.
And, as you stated, you already know state in react shouldn't be mutated.
Upvotes: 1