feat: optimization of render during dnd

This commit is contained in:
2025-08-02 09:56:35 +04:00
parent 586af488da
commit 8ae198897d
4 changed files with 78 additions and 34 deletions

View File

@ -0,0 +1,21 @@
import React, { FC, useMemo } from "react";
import { Box } from "@mantine/core";
import DealCard from "@/app/deals/components/DealCard/DealCard";
import { SortableItem } from "@/components/SortableDnd/SortableItem";
import { DealSchema } from "@/types/DealSchema";
type Props = {
deal: DealSchema;
};
const DealContainer: FC<Props> = ({ deal }) => {
const dealBody = useMemo(() => <DealCard deal={deal} />, [deal]);
return (
<Box>
<SortableItem id={deal.id}>{dealBody}</SortableItem>
</Box>
);
};
export default DealContainer;

View File

@ -5,23 +5,44 @@ import {
verticalListSortingStrategy, verticalListSortingStrategy,
} from "@dnd-kit/sortable"; } from "@dnd-kit/sortable";
import { Box, Stack, Text } from "@mantine/core"; import { Box, Stack, Text } from "@mantine/core";
import DealCard from "@/app/deals/components/DealCard/DealCard"; import DealContainer from "@/app/deals/components/DealContainer/DealContainer";
import { SortableItem } from "@/components/SortableDnd/SortableItem";
import { DealSchema } from "@/types/DealSchema"; import { DealSchema } from "@/types/DealSchema";
import { StatusSchema } from "@/types/StatusSchema";
import { sortByLexorank } from "@/utils/lexorank"; import { sortByLexorank } from "@/utils/lexorank";
type BoardSectionProps = { type BoardSectionProps = {
id: string; id: string;
title: string; status: StatusSchema;
deals: DealSchema[]; deals: DealSchema[];
isDragging?: boolean; isDragging?: boolean;
}; };
const StatusColumn = ({ id, title, deals, isDragging }: BoardSectionProps) => { const StatusColumn = ({ id, status, deals, isDragging }: BoardSectionProps) => {
const { setNodeRef } = useDroppable({ id }); const { setNodeRef } = useDroppable({ id });
const sortedDeals = useMemo(() => sortByLexorank(deals), [deals]); const sortedDeals = useMemo(
() => sortByLexorank(deals.filter(deal => deal.statusId === status.id)),
[deals]
);
console.log("rerender"); const columnBody = useMemo(() => {
return (
<SortableContext
id={id}
items={sortedDeals}
strategy={verticalListSortingStrategy}>
<Stack
gap={"xs"}
ref={setNodeRef}>
{sortedDeals.map(deal => (
<DealContainer
key={deal.id}
deal={deal}
/>
))}
</Stack>
</SortableContext>
);
}, [sortedDeals]);
return ( return (
<Box <Box
@ -37,24 +58,9 @@ const StatusColumn = ({ id, title, deals, isDragging }: BoardSectionProps) => {
userSelect: "none", userSelect: "none",
opacity: isDragging ? 0.5 : 1, opacity: isDragging ? 0.5 : 1,
}}> }}>
{title} {status.name}
</Text> </Text>
<SortableContext {columnBody}
id={id}
items={sortedDeals}
strategy={verticalListSortingStrategy}>
<Stack
gap={"xs"}
ref={setNodeRef}>
{sortedDeals.map(deal => (
<Box key={deal.id}>
<SortableItem id={deal.id}>
<DealCard deal={deal} />
</SortableItem>
</Box>
))}
</Stack>
</SortableContext>
</Box> </Box>
); );
}; };

View File

@ -19,7 +19,14 @@ const StatusColumns = () => {
); );
}; };
return <StatusColumnsDnd onDealDragEnd={onDealDragEnd} />; const onStatusDragEnd = (statusId: number, lexorank: string) => {};
return (
<StatusColumnsDnd
onDealDragEnd={onDealDragEnd}
onStatusDragEnd={onStatusDragEnd}
/>
);
}; };
export default StatusColumns; export default StatusColumns;

View File

@ -11,6 +11,7 @@ import {
KeyboardSensor, KeyboardSensor,
Over, Over,
PointerSensor, PointerSensor,
TouchSensor,
useSensor, useSensor,
useSensors, useSensors,
} from "@dnd-kit/core"; } from "@dnd-kit/core";
@ -36,25 +37,36 @@ type Props = {
statusId: number, statusId: number,
lexorank?: string lexorank?: string
) => void; ) => void;
onStatusDragEnd?: (statusId: number, newRank: string) => void; onStatusDragEnd: (statusId: number, lexorank: string) => void;
}; };
const StatusColumnsDnd: FC<Props> = props => { const StatusColumnsDnd: FC<Props> = props => {
const { statuses, deals, setDeals, setStatuses } = useStatusesContext(); const { statuses, deals, setDeals, setStatuses } = useStatusesContext();
const [activeDeal, setActiveDeal] = useState<DealSchema | null>(null); const [activeDeal, setActiveDeal] = useState<DealSchema | null>(null);
const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null); const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null);
const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]); const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]);
const throttledSetStatuses = useMemo(
() => throttle(setStatuses, 200),
[setStatuses]
);
const throttledSetDeals = useMemo( const throttledSetDeals = useMemo(
() => throttle(setDeals, 200), () => throttle(setDeals, 200),
[setDeals] [setDeals]
); );
const sensorOptions = {
activationConstraint: {
distance: 5,
},
};
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor), useSensor(PointerSensor, sensorOptions),
useSensor(KeyboardSensor, { useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates, coordinateGetter: sortableKeyboardCoordinates,
}) }),
useSensor(TouchSensor, sensorOptions)
); );
const handleDragStart = ({ active }: DragStartEvent) => { const handleDragStart = ({ active }: DragStartEvent) => {
@ -131,7 +143,7 @@ const StatusColumnsDnd: FC<Props> = props => {
const newRank = getNewStatusRank(activeStatusId, overStatusId); const newRank = getNewStatusRank(activeStatusId, overStatusId);
if (!newRank) return; if (!newRank) return;
setStatuses(statuses => throttledSetStatuses(statuses =>
statuses.map(status => statuses.map(status =>
status.id === activeStatusId status.id === activeStatusId
? { ...status, rank: newRank } ? { ...status, rank: newRank }
@ -317,10 +329,8 @@ const StatusColumnsDnd: FC<Props> = props => {
id={`${status.id}-status`}> id={`${status.id}-status`}>
<StatusColumn <StatusColumn
id={`${status.id}-status`} id={`${status.id}-status`}
title={status.name} status={status}
deals={deals.filter( deals={deals}
deal => deal.statusId === status.id
)}
isDragging={activeStatus?.id === status.id} isDragging={activeStatus?.id === status.id}
/> />
</SortableItem> </SortableItem>
@ -332,7 +342,7 @@ const StatusColumnsDnd: FC<Props> = props => {
) : activeStatus ? ( ) : activeStatus ? (
<StatusColumn <StatusColumn
id={`${activeStatus.id}-status`} id={`${activeStatus.id}-status`}
title={activeStatus.name} status={activeStatus}
deals={deals.filter( deals={deals.filter(
deal => deal =>
deal.statusId === deal.statusId ===