Изменение состояния при перетаскивании

#reactjs #react-hooks

Вопрос:

У меня есть список дел, составленный с помощью React. Он состоит из 3 разделов: — «Todo», «В процессе» и «Готово». Я внедрил в этот список функцию перетаскивания. Это работает довольно хорошо. Но после перетаскивания состояние не обновляется. Как я могу обновить состояние после удаления?

Функциональность:

 const [tasks, setTasks] = useState({
todo: [
  {
    _id: 233243424,
    name: "This is a Todo",
  },
  {
    _id: 1123434,
    name: "This is a second Todo",
  },
],
inProgress: [
  {
    _id: 1123243,
    name: "This is a In Progress",
  },
  {
    _id: 55322234,
    name: "This is a second In Progress",
  },
],
done: [
  {
    _id: 2455422,
    name: "This is a Done",
  },
  {
    _id: 33235345,
    name: "This is a second Done",
  },
 ],
});
  let draggableTodo = null;

  const dragStart = (event) => {
    draggableTodo = event.target;
    setTimeout(() => {
      event.target.style.display = "none";
    }, 0);
  };
  const dragEnd = (event) => {
    draggableTodo = null;
    setTimeout(() => {
      event.target.style.display = "block";
    }, 0);
  };

  const dragOver = (event) => event.preventDefault();

  const dragEnter = (event) => {};

  const dragLeave = (event) => (event.target.style.border = "none");

  const dragDrop = (event) => {
   event.preventDefault();
   event.target.appendChild(draggableTodo);
   //Do Something here
  };
 

Структура:

  return (
 <div className="tasks">
  <div className="tasks_container">
    <h1>Tasks</h1>
    <div className="tasks-cont_menu"></div>
    <div className="tasks-cont_body">
      <div
        id="todo_div"
        className="tasks-body_child"
        onDragOver={dragOver}
        onDragLeave={dragLeave}
        onDrop={dragDrop}
      >
        <div className="tasks-bd_title">
          <h3>To Do</h3>
          <div className="tasks-child_status">3</div>
        </div>
        {tasks.todo.map((todo) => {
          return (
            <div
              key={todo._id}
              className="tasks-bd_content"
              draggable={true}
              onDragStart={dragStart}
              onDragEnd={dragEnd}
            >
              {todo.name}
            </div>
          );
        })}
      </div>
      <div
        id="inProgress_div"
        className="tasks-body_child "
        onDragOver={dragOver}
        onDragEnter={dragEnter}
        onDragLeave={dragLeave}
        onDrop={dragDrop}
      >
        <div className="tasks-bd_title">
          <h3>In Progress</h3>
          <div className="tasks-child_status">1</div>
        </div>
        {tasks.inProgress.map((inprogress) => {
          return (
            <div
              key={inprogress._id}
              className="tasks-bd_content"
              draggable={true}
              onDragStart={dragStart}
              onDragEnd={dragEnd}
            >
              {inprogress.name}
            </div>
          );
        })}
      </div>
      <div
        id="done_div"
        className="tasks-body_child"
        onDragOver={dragOver}
        onDragEnter={dragEnter}
        onDragLeave={dragLeave}
        onDrop={dragDrop}
      >
        <div className="tasks-bd_title">
          <h3>Done</h3>
          <div className="tasks-child_status">5</div>
        </div>
        {tasks.done.map((done) => {
          return (
            <div
              key={done._id}
              className="tasks-bd_content"
              draggable={true}
              onDragStart={dragStart}
              onDragEnd={dragEnd}
            >
              {done.name}
            </div>
          );
        })}
      </div>
    </div>
  </div>
</div>
);
 

Например, когда задача перетаскивается в раздел «Готово», tasks состояние должно обновиться, чтобы отразить изменение.

Любая помощь приветствуется!
Спасибо!

Ответ №1:

Я провел рефакторинг и внес некоторые изменения в код, чтобы заставить его работать. Отредактируйте это в CodeSandbox, чтобы узнать, как это работает.

Редактировать элегантно-mcclintock-c9h77


Функциональность:

   const [tasks, setTasks] = useState({
    todo: [
      {
        _id: 233243424,
        name: "This is a Todo"
      },
      {
        _id: 1123434,
        name: "This is a second Todo"
      }
    ],
    inProgress: [
      {
        _id: 1123243,
        name: "This is a In Progress"
      },
      {
        _id: 55322234,
        name: "This is a second In Progress"
      }
    ],
    done: [
      {
        _id: 2455422,
        name: "This is a Done"
      },
      {
        _id: 33235345,
        name: "This is a second Done"
      }
    ],
    dragged: {}
  });
  const draggableTodo = useRef(null);
  const groupName = useRef(null);

  const dragStart = (event) => {
    console.log("drag start");
    const { target } = event;
    const id = target.id;
    const parentElementId = target.parentElement.id;
    setTimeout(() => {
      target.style.display = "none";
      draggableTodo.current = target;
    }, 0);
    setTasks((prevState) => {
      // prevent mutation
      const state = { ...prevState };

      const fn = (name) => {
        groupName.current = name;
        state.dragged = state[name].find((i) => i._id.toString() === id);
      };

      switch (parentElementId) {
        case "todo_div":
          fn("todo");
          return state;
        case "inProgress_div":
          fn("inProgress");
          return state;
        case "done_div":
          fn("done");
          return state;
        default:
          return state;
      }
    });
  };
  const dragEnd = (event) => {
    console.log("dragEnd");
    event.preventDefault();
    draggableTodo.current.style.display = "block";
  };

  const dragOver = (event) => {
    event.stopPropagation();
    event.preventDefault();
  };

  // const dragEnter = (event) => {};

  const dragLeave = (event) => {
    // event.target.style.border = "none";
  };

  const dragDrop = (event) => {
    console.log("drop");

    event.preventDefault();
    event.stopPropagation();
    const { currentTarget } = event;
    const id = currentTarget.id;
    setTasks((prevState) => {
      // This is to not mutate state object
      const state = { ...prevState };
      const fn = (name) => {
        const { current } = groupName;
        const dragged = state.dragged;
        const previousParentId = draggableTodo.current.parentElement.id;
        if (previousParentId !== id) {
          state[current] = state[current].filter((i) => i._id !== dragged._id);
          state[name] = [...state[name], dragged];
        } else {
          draggableTodo.current.style.display = "block";
        }
      };

      switch (id) {
        case "todo_div":
          fn("todo");
          return state;
        case "inProgress_div":
          fn("inProgress");
          return state;
        case "done_div":
          fn("done");
          return state;
        default:
          return state;
      }
    });
  };
 

Структура:

 return (
    <div className="tasks">
      <div className="tasks_container">
        <h1>Tasks</h1>
        <div className="tasks-cont_menu"></div>
        <div className="tasks-cont_body">
          <div
            id="todo_div"
            className="tasks-body_child"
            onDragOver={dragOver}
            onDropCapture={dragDrop}
            style={{ border: "5px solid red" }}
          >
            <div className="tasks-bd_title">
              <h3>To Do</h3>
              <div className="tasks-child_status">3</div>
            </div>
            {tasks.todo?.map((todo) => {
              return (
                <div
                  key={todo._id}
                  className="tasks-bd_content"
                  draggable={true}
                  onDragStart={dragStart}
                  onDragEnd={dragEnd}
                  id={todo._id}
                >
                  {todo.name}
                </div>
              );
            })}
          </div>
          <div
            id="inProgress_div"
            className="tasks-body_child "
            onDragOver={dragOver}
            onDropCapture={dragDrop}
            style={{ border: "5px solid orange" }}
          >
            <div className="tasks-bd_title">
              <h3>In Progress</h3>
              <div className="tasks-child_status">1</div>
            </div>
            {tasks.inProgress?.map((inprogress) => {
              return (
                <div
                  key={inprogress._id}
                  className="tasks-bd_content"
                  draggable={true}
                  onDragStart={dragStart}
                  onDragEnd={dragEnd}
                  id={inprogress._id}
                >
                  {inprogress.name}
                </div>
              );
            })}
          </div>
          <div
            id="done_div"
            className="tasks-body_child"
            onDragOver={dragOver}
            onDropCapture={dragDrop}
            style={{ border: "5px solid green" }}
          >
            <div className="tasks-bd_title">
              <h3>Done</h3>
              <div className="tasks-child_status">5</div>
            </div>
            {tasks.done?.map((done) => {
              return (
                <div
                  key={done._id}
                  className="tasks-bd_content"
                  draggable={true}
                  onDragStart={dragStart}
                  onDragEnd={dragEnd}
                  id={done._id}
                >
                  {done.name}
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
 

Комментарии:

1. Привет! Это сработало! Спасибо

2. Более простым подходом было бы использовать Sortable.js github.com/SortableJS/Sortable

Ответ №2:

грубой идеей было бы использовать обработчик изменений

 const changeHandler = e => {
  setTasks({...tasks, [e.target.name]: e.target.value})
}
 

и использование обработчика событий при перетаскивании

 onChange={changeHandler}
 

Ответ №3:

Вы можете поместить функциональность в функцию перетаскивания, которую вы хотите запустить при удалении div. Параметр события dragEnd должен содержать соответствующие данные, необходимые для корректного обновления состояния.