Reputation: 19
I have array of objects inside my useState hook, and I would like to change one property of each array element let's say it looks like:
const array = [{id:1, isDisplayed: false}, {id:2, isDisplayed: false}, {id:3, isDisplayed: true}]
and while Im trying to use setTimeout
inside useEffect
hook to change property displayed
everywhere where it's not true
to isDisplayed: true
, it waits for the dedicated time, and changes everything at once, what I want to achieve is to change each element with its own delay. I mean something like
const DELAY = 2000
and inside setTimeout for instance setTimeout(() => ... , DELAY * id)
because in place when I render jsx, everything appears all at once, and i just want to make small delays between appearing each element. For instance, first element appears after 2s, second after 3s (not 3s after first one)
My current code looks like:
React.useEffect(() => {
setTimeout(() => {
setArray(array.map((item)=> !item.isDisplayed ? {...item, displayed: true} : item))
}, DELAY * Math.floor(Math.random() * 5);
}, [])
Upvotes: 0
Views: 956
Reputation: 8774
You can set a timeout and trigger an update, if some items are not visible.
And track if new items are revealed with revealed
and if no new items were revealed, stop the flow.
function TodoApp() {
const [items, setItems] = React.useState([
{ id: 1, isDisplayed: false },
{ id: 2, isDisplayed: false },
{ id: 3, isDisplayed: false },
]);
React.useEffect(() => {
let currentTimeout = null;
const displayMoreItems = () => {
setItems(prevItems => {
let revealed = false;
const nextItems = prevItems.map(item => {
if (!revealed && !item.isDisplayed) {
revealed = true;
return { ...item, isDisplayed: true };
}
return item;
});
if (revealed) {
currentTimeout = setTimeout(() => {
displayMoreItems();
}, 1000);
}
return nextItems;
});
};
currentTimeout = setTimeout(() => {
displayMoreItems();
}, 1000);
return () => {
if (currentTimeout) {
clearTimeout(currentTimeout);
}
};
}, [setItems]);
return <div>{items.map(item => (item.isDisplayed ? item.id : null))}</div>;
}
ReactDOM.render(<TodoApp />, document.querySelector('#app'));
Here is a fiddle
Upvotes: 2
Reputation: 2849
const DELAY = 2000;
React.useEffect(() => {
let count = 1;
array.forEach((item) => {
if (!item.displayed) {
setTimeout(() => {
item.displayed = true;
setArray([...array]);
}, DELAY * count);
count ++;
}
})
}, [])
Upvotes: 0