Reputation: 195
I'm trying to set state as array of objects, but it fails.
I created project using CRA, and using react-hooks for states. I get data from graphql server using react-apollo-hooks. I just declared data object in codesandbox, but it doesn't affect my problem.
For every click, set state(array of object) with data(array of object).
const data = {
lists: [
{
id: "1"
},
{
id: "2"
},
{
id: "3"
}
]
};
const Sample = () => {
const [sample, setSample] = useState([]);
const Updator = async () => {
try {
await data.lists.map(list => {
setSample([
...sample,
{
label: list.id,
value: list.id
}
]);
return true;
});
console.log(sample);
} catch (err) {
throw err;
}
};
return (
<div>
<React.Fragment>
<button
onClick={e => {
Updator();
}}
>
Click me
</button>
<p>
<strong>
{sample.map(single => {
return <div>{single.label}</div>;
})}
</strong>
</p>
</React.Fragment>
</div>
);
};
I attached all test code on below.
Here is codesandbox link. https://codesandbox.io/s/zr50rv7qjp
I expect result of
123
by click, but result is
3
Also, for additional click, expected result is
123 123
And I get
3 3.
When I use setSample(), I expect function something like Array.push(). But it ignores all the previous data expect the last one.
Any helps will be thankful!
Upvotes: 2
Views: 131
Reputation: 282000
state updater does batching
and since you are calling the setSample
method in map, only your last value is being written in state.
The best solution here is to return data from map and then update the state once like below.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const data = {
lists: [
{
id: "1"
},
{
id: "2"
},
{
id: "3"
}
]
};
const Sample = () => {
const [sample, setSample] = useState([]);
const Updator = async () => {
try {
const newData = data.lists.map(list => {
return {
label: list.id,
value: list.id
};
});
setSample([...sample, ...newData]);
} catch (err) {
throw err;
}
};
return (
<div>
<React.Fragment>
<button
onClick={e => {
Updator();
}}
>
Click me
</button>
<p>
<strong>
{sample.map((single, index) => {
return <div key={index}>{single.label}</div>;
})}
</strong>
</p>
</React.Fragment>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Sample />, rootElement);
Another solution is to use the callback method to update state but you must avoid calling state updates multiple times.
Upvotes: 2
Reputation: 4439
You're destructing sample
which will not have the latest version of itself when you're looping and calling setSample
. This is why it only puts 3 in the list of samples, because the last iteration of the map will destruct an empty sample list and add 3.
To make sure you have the newest value of sample you should pass a function to setSample
. This function will get the latest version of your state var from react.
setSample((latest) => {
return [
...latest,
{
label: list.id,
value: list.id
}
]
});
Upvotes: 1