Reputation: 1040
I've created a React component with an array state state.colors
that contains three hex values (initial state is ['#aaaaaa', '#aaaaaa', '#aaaaaa']
). Each value in the state.colors
array can be updated using color pickers. As a test, I've updated the first color to red, second to green, and third to blue. This works as expected in a class component (as shown in the screenshot below).
class EditArtworkClass extends React.Component {
constructor(props) {
this.state = {
colors: initialPalette
};
}
setColor(index, hex) {
const clonedColors = _.clone(this.state.colors);
clonedColors[index] = hex;
this.setState(
{
colors: clonedColors
},
() => {
console.log("updated");
}
);
}
// other code here
}
Class Component - Before choosing colors:
Class Component - After choosing colors (state.colors
have the correct hex values):
However, I am facing an issue when using a functional component. Every time I update a color using the color picker, all other values in the colors
state array are reset to the initial value (#aaaaaa
). If I set the first color to red, second to blue, and third to green, only the last value in the colors
array will have the correct hex since the other two values are reset to #aaaaaa
when updating the third color.
export default function EditArtworkFunctional() {
const [colors, setColors] = useState(initialPalette);
const setColor = (index, hex) => {
const clonedColors = _.clone(colors);
clonedColors[index] = hex;
return clonedColors;
};
// Other code here
}
Functional Component - Before choosing colors:
Functional Component - After choosing colors (only the last color I pick has a correct hex in the state):
Note that the color picker is an uncontrolled component and will display the colors you pick, not the color in the colors
state array.
I have created a reproducible mini-app in the Codesandbox Link below.
Condesandbox Link: https://codesandbox.io/s/class-vs-functional-component-array-state-8lnzd
I have zero ideas as to why this is happening, so any help or guidance would be greatly appreciated 🤯.
UPDATE: I've fixed the problem using @TalOrlanczyk's answer. The trick was to retrieve the previous state using the optional parameter when updating. You can view the CodeSandbox repo of the working version here.
Upvotes: 3
Views: 2123
Reputation: 1213
I think I solve the problem
const setColor = (index, hex) => {
setColors((prev) => {
console.log(prev);
const clonedColors = [...prev];
clonedColors[index] = hex;
return clonedColors;
});
};
like @theWellHopeErr says it cause because it happend because you didn;t send it as a spread operator it's happend because array is reference if you change the same reference it's maybe change the value but the use state don't catch it because it's not a new reference.
Another thing you should know that is much better to use the like this (with the prev) because like that you can be sure you get the real last input you insert into this state
UPDATE
The spread operator is good only for shallow only when it's deep one like objects with a property with object this will not work and change the original state also.
don't mutates the current state, this is an anti-pattern in React this is a thread that explain it perfectly
Clone array to do deep clone you should use
a good artical in medium that explain about shallow and deep clone
Upvotes: 4
Reputation: 1872
This is because of two reasons
const clonedColors = _.clone(colors)
using this deep clones the array which is not what we want, we have to make a shallow copy of the array . So, const clonedColors = colors
will be the right way.setColors(clonedColors)
does not work correctly, but if we setState
using spread syntax setColors([...clonedColors])
it will. This is because the array is just a reference, and React setState
won't rerender if the state doesn't get a new reference. That's why we send a new array reference by using the spread syntax [...clonedColors]
. Thanks to @TalOrlanczyk for the reason.const setColor = (index, color) => {
const clonedColors = colors;
clonedColors[index] = color;
setColors([...clonedColors]);
console.log(colors);
};
Here is the Updated Codesandbox Link
Upvotes: 4