Home > Enterprise >  How to make a stateful array with elements updating their own state individually in React?
How to make a stateful array with elements updating their own state individually in React?

Time:07-30

import { useEffect, useState } from "react";
import { v4 as uuidv4 } from 'uuid';

interface Item {
  id: string,
  value: number
}

function App() {
  const [list, setList] = useState<Item[]>([]);

  useEffect(() => {
    console.log(list);
  }, [list])

  function createItem() {
    const id = uuidv4();
    setList([...list, { id: id, value: 1 }]);
    return id;
  }

  function updateItem(index: number, value: number) {
    var newArray = [...list];
    newArray[index].value = value;
    setList(newArray);
  }

  async function process() {
    const id = createItem()
    const index = list.findIndex(item => item.id === id);

    // Error starts here because list is empty (?) 

    await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 10000)));
    updateItem(index, 2);
    await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 10000)));
    updateItem(index, 3)
    await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 10000)));
    updateItem(index, 4)
    await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 10000)));
    updateItem(index, 5)
    await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 10000)));
  }

  return (
    <div className="App">
      <button onClick={() => {
        process();
      }}>Create</button>
      {list.map((item) => {
        return <p key={item.id}>{item.value}</p>
      })}
    </div>
  );
}

export default App;

I am trying to make something like a stateful array but all array elements should update their own state individually.

Assume: User clicks create, a new number appears on screen starting from 1. It increments itself every x seconds. When user clicks Create again, same applies for newly created number but old number should also continue incrementing itself. Assume user clicked Create 100 times, all numbers should manage their own state and I should show it in screen.

But when I use code above it gives error just after trying to find index. There was a time it was working but then items were overriding each other's state because of [...list, item]. It is probably because previously created ones doesn't know about new ones.

I am totally confused. I just want to have a stateful array where all elements individually updating their own state.

One workaround is creating a new component for Item and having it store/update it's own data but I need to do this without creating a new component.

CodePudding user response:

I had to refactor your code mainly because of following reasons.

Here:

const id = createItem()
const index = list.findIndex(item => item.id === id);

When you append item to list inside createItem, it is not immediately available on list where you are trying to do findIndex.

Also everywhere you are using setState now it is better to use functional form, to get access to previous state.

Check the code:

export default function App() {
  const [list, setList] = React.useState([]);

  React.useEffect(() => {
    console.log(list);
  }, [list]);

  function createItem() {
    const id = uuidv4();
    setList((ps) => [...ps, { id, value: 1 }]);
    return id;
  }

  function updateItem(id, value) {
    setList((ps) => ps.map((x) => (x.id === id ? { ...x, value } : x)));
  }

  async function process() {
    const id = createItem();

    for (let i = 2; i <= 5; i  ) {
      await new Promise((resolve) =>
        setTimeout(resolve, Math.floor(Math.random() * 10000))
      );
      updateItem(id, i);
    }
  }

  return (
    <div className="App">
      <button
        onClick={() => {
          process();
        }}
      >
        Create
      </button>
      {list.map((item) => {
        return <p key={item.id}>{item.value}</p>;
      })}
    </div>
  );
}
  • Related