2

I am having trouble getting react-beautiful-dnd to work because children of the draggable are flickering after reordering:

ezgif-4-bd9d8ec129

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)
felixmp
  • 307
  • 3
  • 16

1 Answers1

0

In this specific case the problem was caused by the Polaris library and I have not found a solution to it.

I avoided the issue by switching to vanilla checkboxes (<input/>) which look almost identical.

felixmp
  • 307
  • 3
  • 16