Как в ReactJS удалить компонент из массива, чтобы он обновился и перерисовался?

Здравствуйте! Прошу прощения за беспокойство, но я столкнулся с проблемой в своём проекте. Я — начинающий разработчик, и мне нужно немного помощи. У меня есть недоделанный проект, который можно найти по ссылке <a href="https://github.com/m3tmrphss/example" rel="nofollow">https://github.com/m3tmrphss/example</a>. 

В структуре проекта в папке `src/components/main/main-components` есть две папки: `backlog` и `ready`, каждая из которых содержит по одному компоненту React. Я уже настроил два взаимодействующих списка, где в `backlog` я добавляю задачи, а затем могу перемещать их в `ready`. Проблема в том, что задача из `backlog` не удаляется автоматически при перемещении её в `ready` — она удаляется только после нажатия кнопки "Добавить задачу". 

Я попробовал множество решений, использовал `useContext`, но не достиг желаемого результата. Ранее также пытался обновлять массив через `useEffect`, но это не сработало. Буду очень признателен за любую помощь или совет по решению этой проблемы!
  • 8 сентября 2024 г. 17:33
Ответы на вопрос 2
Здравствуйте!

Ваша проблема связана с тем, как вы управляете состоянием ваших компонентов в React. Чтобы удалить задачу из одного списка (например, `backlog`) при её перемещении в другой список (например, `ready`), нужно правильно обновить состояние, которое хранит ваши задачи.

Предположим, у вас есть массив задач в компоненте `backlog`, и вы хотите удалить задачу из этого массива, когда она перемещается в `ready`. Вот пошаговая инструкция, как это сделать:

1. **Убедитесь, что вы управляете состоянием для обеих списков.** Это может быть сделано в родительском компоненте, который будет хранить и передавать задачи в компоненты `backlog` и `ready`.

2. **Создайте функции для удаления задач из `backlog`.** Эта функция будет принимать идентификатор задачи и удалять её из состояния.

3. **При перемещении задачи из `backlog` в `ready` вызывайте эту функцию удаления.** Таким образом, задача будет удалена из массива `backlog`, и компонент снова перерисуется.

Вот примерный код, который иллюстрирует этот подход:

```jsx
import React, { useState } from 'react';
import Backlog from './backlog/Backlog';
import Ready from './ready/Ready';

const App = () => {
  // Состояния для задач
  const [backlogTasks, setBacklogTasks] = useState([{ id: 1, text: 'Task 1' }, { id: 2, text: 'Task 2' }]);
  const [readyTasks, setReadyTasks] = useState([]);

  // Функция для перемещения задачи в готовую
  const moveToReady = (taskId) => {
    const taskToMove = backlogTasks.find(task => task.id === taskId);
    if (taskToMove) {
      // Удаляем задачу из backlog
      setBacklogTasks(backlogTasks.filter(task => task.id !== taskId));
      // Добавляем задачу в ready
      setReadyTasks([...readyTasks, taskToMove]);
    }
  };

  return (
    <div>
      <h1>Backlog</h1>
      <Backlog tasks={backlogTasks} onMoveToReady={moveToReady} />
      <h1>Ready</h1>
      <Ready tasks={readyTasks} />
    </div>
  );
};

export default App;
```

В компоненте `Backlog`, вы могли бы использовать `onMoveToReady` вот так:

```jsx
const Backlog = ({ tasks, onMoveToReady }) => {
  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>
          {task.text} 
          <button onClick={() => onMoveToReady(task.id)}>Move to Ready</button>
        </li>
      ))}
    </ul>
  );
};
```

В этом примере, когда вы нажимаете кнопку "Move to Ready", вызывается функция `moveToReady`, которая удаляет задачу из `backlogTasks` и добавляет её в `readyTasks`. Это вызывает перерисовку компонентов, и задача исчезает из списка `backlog`.

Убедитесь, что ваше состояние правильно обновляется в соответствии с этим подходом. Если у вас появятся дополнительные вопросы или вы столкнётесь с конкретными проблемами в коде, не стесняйтесь делиться ими! Удачи в разработке!
1. Зачем вам нужен тут Context? Добавьте useState со списком задач в main компонент и передавайте их в дочерние компоненты: 
export function MainNode() {
    const [tasks, setTasks] = useState(predefinedTasks)

    const addTask = (listName, task) => {
       // ..
       // setTasks 
       // ..
    }

    const removeTask = (listNaem, taskId) => {
        // ...
        // setTasks
        // ..
    }

    
    const moveTask = (fromListName, toListName, taskId) => {
        // ..
        // setTasks
        // ..
     }

    return (
        <main>
            <BacklogTasksContainer tasks={tasks.backlog} addTask={addTask}/>
            <ReadyTasksContainer ... />
            <InProgressTasksContainer ... />
            <FinishedTasksContainer ... /> 
        </main>
    )
}


2. Если вам необходимо по каким-то причинам использовать context, то по аналогии с примером выше:
export const TaskContext = createContext();

export const TaskProvider = ({ children }) => {
    const [tasks, setTasks] = useState(predefinedTasks)

    const addTask = (listName, task) => {
       // ..
       // setTasks 
       // ..
    }

    const removeTask = (listNaem, taskId) => {
        // ...
        // setTasks
        // ..
    }

    
    const moveTask = (fromListName, toListName, taskId) => {
        // ..
        // setTasks
        // ..
     }

    return (
        <TaskContext.Provider value={{ tasks, moveTask, addTask }}>
            {children}
        </TaskContext.Provider>
    );
}


После чего используйте:
const TaskList = ({ listName }) => {
    const { tasks, moveTask, addTask } = useContext(TaskContext);
    // ....
}


Вам надо почитать как работает реактивность в React и разобраться почему изменения в объекте не триггерят рирендер компонента. Если по простому, то Вам нужно не просто добавлять (push) новый task в список, а полностью заменять весь объект (data) с тасками и не использовать для каждого компонента свой useState
// так не работет
tasks.backlog.push(...) 

// так работает
setTasks(currentState => {
    currentState.backlog.push(...)
    return currentState
})
Похожие вопросы