I'm facing a problem, I have a React state which is an array.
When an item is added to the array with setState, I would like to delete it after for example five seconds.
Something like :
const {messages, setMessages} = useState([])
useEffect(() => {
const timer = setTimeout(() => {
setMessages(arr => arr.splice(i, 1))
}, 5000)
return () => clearTimeout(timer)
})
Where i
is the index of the item to delete.
As a concrete example: I click every second in a button, this add an item to the array. After 5 seconds, the first item is deleted. After 6 seconds, the second item is deleted. etc...
Thanks for your help.
CodePudding user response:
Array.prototype.splice
mutates the array in place, meaning the messages
state array reference never changes and React bails out of rerendering.
The
splice()
method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the
Object.is
comparison algorithm.)
You can use Array.prototype.filter
to shallow copy the previous messages
array into a new reference, except for the element at the specified matching index.
useEffect(() => {
const timer = setTimeout(() => {
setMessages(arr => arr.filter((_, index) => index !== i)) // *
}, 5000);
return () => clearTimeout(timer);
}, []); // <-- don't forget dependency array!
* assumes i
is defined in scope
CodePudding user response:
You could set each item in the array as an object with a timeRemaining
property and then use setInterval
to reduce the time every second until it eventually hits 0, at which point you would delete the item.
useEffect(()=>{
const interval = setInterval(()=>{
setMessages(prev=>prev.filter(i=>i.timeRemaining>0).map((item)=>{
return {
...item, timeRemaining: item.timeRemaining - 1
}
}))
},1000)
return ()=> clearInterval(interval)
},[])