#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, чтобы узнать, как это работает.
Функциональность:
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 должен содержать соответствующие данные, необходимые для корректного обновления состояния.