krissXawera
krissXawera

Reputation: 19

setTimeout inside useEffect with useState

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

Answers (2)

Domino987
Domino987

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

Prime
Prime

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

Related Questions