#reactjs #react-hooks #signalr #bootstrap-modal #react-dnd
Вопрос:
Я пытаюсь смоделировать плату Trello с помощью reactjs и SignalR (.NETCore). Я создал доску со списком карт, и каждый элемент карты содержит данные карты, которые являются всплывающими. эти данные карты всплывают, когда пользователь нажимает на название карты. Я использую SignalR (Websocket), чтобы заставить многопользовательские пользователи взаимодействовать на одной доске, прослушивая событие, которое перерисовывает всю доску, когда любой пользователь меняет карту. Проблема возникает, когда : Пользователь A открывает всплывающее окно для задачи 1, а пользователь B перетаскивает задачу 2 из списка задач, например, в список выполняемых задач. когда это действие произошло, всплывающее окно закрылось и снова открылось, поскольку вся доска trello была перенаправлена на основе события, которое прослушивает каждый пользователь. Как я могу исправить эту проблему с открытием и закрытием всплывающего окна и сохранить его открытым, как есть ?? и если логика неверна, пожалуйста, не стесняйтесь сообщить мне об этом!
примечание: Я использую пакет react-beautiful-dnd для функции перетаскивания.
Решетчатая доска
<DragDropContext
onDragEnd={(result) => {
ondragend(result);
}}
>
<div className="App">
<div>
{
sprintLoading ?
sprints.map((card, index) => (
// our sprints
<TrelloList
sprintType={card.sprintType}
index={index}
listId={card.sprintId}
sprintSchemaId={card.sprintSchemaId}
key={card.sprintId}
title={card.sprintName}
cards={card.tasks}
/>
))
: null
}
</DragDropContext >
);
}
Список Трелло
useEffect(async () => {
}, [tasks, socketState, trelloRerender]);
return (
<Droppable droppableId={String(listId)} >
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={styles.container}>
<h4>{title}</h4>
{cards ? cards.map((card, index) =>
(
// our tasks
<TrelloCard
sprintType={sprintType}
listId={listId}
index={index}
cardId={card.taskId}
key={card.taskId}
text={card.taskDescription}
projectId={card.projectId}
userId={card.userId}
sprintId={card.sprintId}
sprintSchemaId={card.sprintSchemaId}
title={card.taskName}
schemaId={card.sprintSchemaId}
groupId={card.groupId}
taskStatus={card.taskStatus}
userName={card.user ? card.user.userName : ''}
taskCreatedDate={card.taskCreatedDate}
taskDueDate={card.taskDueDate}
comments={card.comments}
/>
)) : null}
{provided.placeholder}
</div>
)}
</Droppable>
)
};
Trello card
const { showTaskDetails, setShowTaskDetails } = useContext(ShowTaskDetailsContext);
useEffect(async () => {
await axios.get(`${BACKEND_URL}/tasks/${cardId}`).then(res => {
console.log(res.data.comments);
setCurrentTaskComments(res.data.comments)
console.log(currentTaskComments);
});
}, [rerenderForComments]);
useEffect(async () => {
const sprintId = location.pathname.split('/')[2];
setRerenderforCards(false);
setSprintId(sprintId);
setIsRolledback(false);
await fetchObject(`${BACKEND_URL}/sprintschemas/${sprintSchemaId}`)
.then(res => {
if (res.sprintTaskRollback !== null) {
setIsRolledback(true);
}
})
.catch(err => console.log(err));
}, []);
const handleShowTaskDetails = () => setShowTaskDetails(true);
const handleCloseTaskDetails = () => setShowTaskDetails(false);
return (
<Draggable draggableId={String(cardId)} index={index} key={cardId} >
{provided => (
// as provided.innerdiv need to refer to an action DOM node and card is a material-ui node
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps} >
<Card style={styles.cardContainer} >
{/* testing generic hooks */}
<CardContent>
<Typography component={'div'}
gutterBottom>
<span
// href={`/loboard/${sprintId}`}
style={{ display: "block", fontWeight: "bold" }}
onClick={handleShowTaskDetails}
>
{title}
</span>
<TrelloCardDetails
showTaskDetails={showTaskDetails}
handleCloseTaskDetails={handleCloseTaskDetails}
title={title}
description={text}
comments={currentTaskComments}
assignto={userName}
cardId={cardId}
handleDropDownchanged={handleDropDownchanged}
handleMarkCompleted={handleMarkCompleted}
markCompletedBtnTxt={markCompletedBtnTxt}
markBtnStyle={markBtnStyle}
userId={userId}
setComments={setCurrentTaskComments}
rerenderForComments={rerenderForComments}
setRerenderForcommented={setRerenderForcommented}
/>
{/* onchange == move task to it's new list of cards */}
<Form.Group className="mt-2" controlId="exampleForm.ControlSelect2">
<Form.Row>
<Form.Label column="sm" sm={4}>Move to:</Form.Label>
<Col>
{/* adding default value to reset */}
<Form.Control size="sm" as="select"
onChange={handleDropDownchanged}
defaultValue={'select'}
>
<option disabled value="select">select status</option>
<option value="backlog" name='setBacklog'>backlog</option>
<option value="todo" name='setTodo'>todo</option>
<option value="inprogress" name='setInProgress'>inprogress</option>
<option value="done" name='setInProgress'>done</option>
</Form.Control>
</Col>
</Form.Row>
</Form.Group>
<Form.Group>
<Form.Row>
<Form.Label column="sm" sm={4}>assigned to:</Form.Label>
<Col>
<Form.Control
type="text"
disabled
value={userName} />
</Col>
</Form.Row>
</Form.Group>
<Row>
<Button className={markBtnStyle}
onClick=
{() =>
handleMarkCompleted(cardId)
}
style={{ display: "block" }}
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" className="bi bi-check" viewBox="0 0 16 16">
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z" />
</svg>
{markCompletedBtnTxt}
</Button>
</Row>
{/* <Row>
<Col> */}
<span className="text-sm float-right mt-2">
<small title="created at" className="text-muted d-block ">
<BsCalendar /> {taskCreatedDate.split('T')[0]}
</small>
</span>
</Typography>
</CardContent>
</Card>
</div>
)}
</Draggable>
)
Компонент сведений о карте
export const TrelloCardDetails = ({
showTaskDetails,
description,
comments,
title,
handleCloseTaskDetails
, assignto, cardId,
handleDropDownchanged,
handleMarkCompleted,
markBtnStyle,
markCompletedBtnTxt,
userId,
setComments, rerenderForComments,
setRerenderForcommented
}) => {
useEffect(() => {
console.log("card details")
}, [comments]);
return (
<div>
<Modal
show={showTaskDetails}
onHide={handleCloseTaskDetails}>
<Modal.Header closeButton>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Group className="mt-2" controlId="exampleForm.ControlSelect2">
<Form.Row>
<Form.Label column="sm" sm={4}>Move to:</Form.Label>
<Col>
{/* adding default value to reset */}
<Form.Control size="sm" as="select"
onChange={handleDropDownchanged}
defaultValue={'select'}
>
<option disabled value="select">select status</option>
<option value="backlog" name='setBacklog'>backlog</option>
<option value="todo" name='setTodo'>todo</option>
<option value="inprogress" name='setInProgress'>inprogress</option>
<option value="done" name='setInProgress'>done</option>
</Form.Control>
</Col>
</Form.Row>
</Form.Group>
<Form.Group>
<Form.Row>
<Form.Label column="sm" sm={4}>assigned to:</Form.Label>
<Col>
<Form.Control
type="text"
disabled
value={assignto} />
</Col>
</Form.Row>
</Form.Group>
<Form.Group>
<Form.Row>
<Form.Label column="sm" sm={4}>Description: </Form.Label>
<Col>
<Form.Control style={{ resize: 'none' }} as="textarea" value={description} disabled />
</Col>
</Form.Row>
</Form.Group>
<Row>
<Button
className={markBtnStyle}
onClick=
{() =>
handleMarkCompleted(cardId)
}
style={{ display: "block" }}
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" className="bi bi-check" viewBox="0 0 16 16">
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z" />
</svg>
{markCompletedBtnTxt}
</Button>
</Row>
{/* <div style={{marginTop:"20px", backgroundColor: "#e9ecef", opacity: "1", border: "1px solid #ced4da", borderRadius: "5px" }}> */}
<CommentList rerenderForComments={rerenderForComments} setRerenderForcommented={setRerenderForcommented} assignto={assignto} comments={comments} setComments={setComments} taskId={cardId} userId={userId} />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleCloseTaskDetails}>
Close
</Button>
<Button variant="primary" onClick={handleCloseTaskDetails}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div >
);
}
Комментарии:
1. Где
showTaskDetails
обновляется и инициализируется?2. @gpichot отредактировано. Я добавляю его с контекстом, извините, что я его уронил, пытаясь максимально сократить код, чтобы его было не так много читать.