I am having trouble getting react-beautiful-dnd to work because children of the draggable are flickering after reordering:
No external requests happen in the background, the app stores the change only in the local state (see first code block reorderElements()
).
What really confuses me is how only the 2 checkboxes flicker, but not the text or the icon.
The list rerenders only once after reorder (asynchronously when the state is updated)
I suspect the asynchronous nature of useState to be the issue. But the fact that only those 2 checkboxes flicker contradicts the suspicion in my opinion.
The draggableId
is updated after each reorder to the index of each element.
Here is the code:
My reorder function that is called onDragEnd
:
function reorderElements({ source, destination }: any) {
if (!destination) {
return;
}
setAppState((oldAppState: AppState | null) => {
if (oldAppState) {
const { content } = oldAppState;
const copy = JSON.parse(JSON.stringify(content));
const [elementToMove] = copy.splice(source.index, 1);
copy.splice(destination.index, 0, elementToMove);
const elementsWithNewIndex = copy.map(
(e: BannerElement, i: number) => ({
...e,
id: "" + i,
})
);
return { ...oldAppState, content: elementsWithNewIndex };
}
return null;
});
}
The list component
<DragDropContextContainer onDragEnd={onDragEnd}>
<DroppableContainer direction="horizontal" droppableId="root">
{(provided: DroppableProvided) => (
<div
style={{ display: "flex" }}
ref={provided.innerRef}
{...provided.droppableProps}
className="draggable-container-custom"
>
<>
{content.map((element, index) => (
<Element
key={index}
index={index}
element={element}
updateElement={updateElement}
deleteElement={deleteElement}
provided={provided}
nElements={nElements}
/>
))}
{provided.placeholder}
</>
</div>
)}
</DroppableContainer>
</DragDropContextContainer>
DragDropContextContainer
export function DragDropContextContainer({ children, ...props }: Props) {
return <DragDropContext {...props}>{children}</DragDropContext>;
}
DroppableContainer
export function DroppableContainer({ children, ...props }: Props) {
return <Droppable {...props}>{children}</Droppable>;
}
Each Element
<DraggableContainer draggableId={id} index={index} key={index}>
{(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => {
const draggableStyle = snapshot.isDragging
? { ...customDraggableStyle, ...provided.draggableProps.style }
: provided.draggableProps.style;
return (
<div
ref={provided.innerRef}
{...provided.draggableProps}
style={draggableStyle}
className="draggable-custom"
>
<div
{...provided.dragHandleProps}
className="element-drag-icon-container"
>
<div className="element-drag-icon">
<PolarisIcon source={DragHandleMinor} color={"base"} />
</div>
</div>
<div className="element-container">
<div className="element-icon-container">
<Checkbox
label="Text has Icon Prefix"
checked={hasIcon}
onChange={(newValue) => {
updateElement({ id, hasIcon: newValue });
}}
/>
<Icon
disabled={!hasIcon}
key={JSON.stringify(icon)}
id={id}
elementIcon={icon}
updateElement={updateElement}
/>
</div>
<div className="element-text-container">
<TextField
label="Set your Text:"
multiline={3}
value={text}
onChange={(text: ElementText) => {
updateElement({ id, text });
}}
autoComplete="off"
/>
</div>
<div className="element-bottom-row">
<Checkbox
label="Show on mobile"
checked={showMobile}
onChange={() => {
updateElement({ id, showMobile: true });
}}
/>
<Button
plain
destructive
onClick={() => {
deleteElement(id);
}}
>
delete
</Button>
</div>
</div>
</div>
);
}}
</DraggableContainer>
);
DraggableContainer
export function DraggableContainer({ children, ...props }: Props) {
return <Draggable {...props}>{children}</Draggable>;
}
Those container-Components exist to get rid of some ESLint errors.
- Console logs nothing regarding this package.
- im using
"react": "^17.0.2"
and"react-beautiful-dnd": "^13.1.0"
- tested in Chrome
Version 108.0.5359.124 (Official Build) (arm64)