Home > OS >  React: how to asynchronously and sequentially remove elements from array state?
React: how to asynchronously and sequentially remove elements from array state?

Time:09-11

I'm having hard time finding a way to solve quite simple problem in React:

import React, { useState } from 'react';
import './style.css';

const removeTodoFromDb = (todo) =>
  new Promise((resolve) => {
    setTimeout(resolve, 1000);
  });

export default function App() {
  const [todos, setTodos] = useState(['sleep', 'work', 'eat', 'play']);

  const cleanup = async () => {
    console.log('cleanup()');
    if (todos.length) {
      console.log('1', todos.join('|'));
      todos[0] = todos[0]   '*';
      setTodos((t) => todos.slice());
      await removeTodoFromDb(todos.shift());
      setTodos((t) => todos.slice());
      cleanup();
      console.log('2', todos.join('|'));
    }
  };

  return (
    <>
      {todos.map((t, i) => (
        <div key={i}>{t}</div>
      ))}
      <button onClick={() => cleanup()}>Clean up</button>
    </>
  );
}

StackBlitz url: https://stackblitz.com/edit/react-aa6wde

I know todos, and setTodos loose references, but I cannot put cleanup() function into the useEffect block as it won't be exposed to the DOM.

What is the proper pattern for actions like this?

Thanks

CodePudding user response:

If I understand correctly, you want to remove all elements of an array one after another, right?

If so, here's how I would write the cleanup function:

const cleanup = async () => {
  if (!todos.length) return

  // Use Promise.all to call removeTodoFromDb for ALL todos at the SAME time
  await Promise.all(todos.map(todo => removeTodoFromDb(todo))
  setTodos([])
};

Not sure you mean by this:

I know todos, and setTodos loose references, but I cannot put cleanup() function into the useEffect block as it won't be exposed to the DOM.

CodePudding user response:

If you want put cleanup in useEffect just do it. Activate useEffect when some boolean = true and with help onClick change it.

  • Related