I am trying to keep track of an array of clicked IDs. Whenever I click a new ID (after clicking the first one), it just replaces the old one in the array instead of adding it to the array. I even tried to sanity check by doing the React example of incrementing a "count" state, and that just stays at 1, so clearly I am not sane LOL. Help please!
const Example = () => {
const [downloadedBatches, setDownloadedBatches] = useState<any>([]);
const [count, setCount] = useState<number>(0);
const _handleDownload = ({ currentTarget: { dataset: { id } } }: any) => {
// TODO: Add call
if (!downloadedBatches.includes(id)) {
setDownloadedBatches([...downloadedBatches, id])
}
console.log(`Downloaded Batch #${id}`)
}
const _testCount = () => setCount(count 1);
return (
<Button icon={'download'} onClick={_handleDownload} data-id={1}/>
<Button icon={'download'} onClick={_handleDownload} data-id={2}/>
<Button icon={'download'} onClick={_handleDownload} data-id={3}/>
<Button icon={'download'} onClick={_testCount} data-id={3}/>
)
}
CodePudding user response:
You can try setting the state with a Callback.
setDownloadedBatches((prevState) => [...prevState, id])
CodePudding user response:
I don't see any overt issues with the shared code snippet but I suspect the Button
component is doing something like memoizing the passed onClick
handler it when it mounts. Regardless of this it's often the case in React you may be working with stale enclosures over a React state value from one render cycle when executing a callback in a later render cycle (after the state has since updated). To resolve issues like these you should use functional state updates. Functional updates are where you pass a callback function to the state updater function. When the enqueued state update is processed the previous state's value is passed to the callback to be updated from.
Example:
const _handleDownload = ({ currentTarget: { dataset: { id } } }: any) => {
setDownloadedBatches(downloadedBatches => {
if (!downloadedBatches.includes(id)) {
// shallow copy previous state and append new id value
return [...downloadedBatches, id];
}
// else just return previous state value, nothing to update
return downloadedBatches;
});
}
const _testCount = () => setCount(
// return previous state value 1
count => count 1
);
Code:
const Example = () => {
const [downloadedBatches, setDownloadedBatches] = useState<any>([]);
const [count, setCount] = useState<number>(0);
const _handleDownload = ({ currentTarget: { dataset: { id } } }: any) => {
// TODO: Add call
setDownloadedBatches(downloadedBatches => {
if (!downloadedBatches.includes(id)) {
return [...downloadedBatches, id];
}
return downloadedBatches;
});
console.log(`Downloaded Batch #${id}`);
}
const _testCount = () => setCount(count => count 1);
return (
<Button icon={'download'} onClick={_handleDownload} data-id={1}/>
<Button icon={'download'} onClick={_handleDownload} data-id={2}/>
<Button icon={'download'} onClick={_handleDownload} data-id={3}/>
<Button icon={'download'} onClick={_testCount} data-id={3}/>
);
};
CodePudding user response:
you can't update state directly, try like this instead:
const _handleDownload = ({ currentTarget: { dataset: { id } } }: any) => {
// TODO: Add call
if (!downloadedBatches.includes(id)) {
// make a copy of the array
let copy = [...downloadedBatches];
// push to the array
copy.push(id);
// set the state
setDownloadedBatches(copy);
}
console.log(`Downloaded Batch #${id}`)
}