rona
rona

Reputation: 11

react-beautiful-dnd: Prevent flicker when drag and drop a lists

I'm using this react-beautiful-dnd library to be able to reorder lists. However, even though I'm able to drag and drop and re-order, there is a flicker when I try to reorder lists. You can see in the video: enter image description here

Here is my code: I added sorting to the array by order before mapping.

const Board = () => {
const [currentId, setCurrentId] = useState(null);

const lists = useSelector((state) => state.lists);
const dispatch = useDispatch();

const classes = useStyles();

useEffect(() => {
    dispatch(getLists());
}, [currentId, dispatch]);

const onDragEnd = (result) => {
    const { destination, source, draggableId, type } = result;

    if(!destination) return;

    const droppableIdStart = source.droppableId;
    const droppableIdEnd = destination.droppableId;
    const droppableIndexStart = source.index;
    const droppableIndexEnd = destination.index;

    const newState = [...lists];

    // drag lists
    if(type === 'list') {
        const dragList = newState.splice(droppableIndexStart, 1);
        newState.splice(droppableIndexEnd, 0, ...dragList);
        
        // update order list to be index
        newState.forEach((list, index) => {
            list.order = index;
            dispatch(updateList(list._id , { ...list }));
        });
    }
    return newState;
}

// Arranging lists by order
const newArrange = (a,b) => {
    return (a.order - b.order);
}
lists.sort(newArrange);

return (
    <>
    <DragDropContext  onDragEnd={onDragEnd} >
        <div>
            <h1>Board</h1> 
            <Droppable droppableId="all-lists" direction="horizontal" type="list">
                { provided => (
                    <div className={classes.listContainer} {...provided.droppableProps} ref={provided.innerRef} >
                    { lists.map((list, index) => 
                        (user?.result?.googleId === list?.creator || user?.result?._id === list?.creator) ? 
                        <List key={list._id} title={list.title} cards={list.cards} currentId={list._id} index={index} /> :
                        null
                    )}
                        {addListFlag && (
                            <InputItem 
                                value={listData.title}
                                btnText={"Add list"} 
                                type={"list"} 
                                placeholder={"Enter a list title..."}
                                changedHandler={handleChange} 
                                closeHandler={closeHandlerBtn}
                                addItem={submitHandler}
                                />
                        )}
                        {!addListFlag && (
                            <AddBtn btnText={"Add another list"} type={"list"} handleClick={handleAddition} />
                        )}
                        {provided.placeholder}
                    </div>
                )}
            </Droppable>
        </div>
    </DragDropContext>
    </>
    )
}

export default Board;

Sample of data:

{
    _id: "6163cdd306d27b",
    title: "a",
    name: "first one",
    order: "0",
    cards:[
        {
            id: "0",
            text: "a1",
            _id: {_id: "61d0ca2c20d27e"}
        },
        {
            id: "1",
            text: "a2",
            _id: {_id: "616541ec90630"}
        },
        {
            id: "2",
            text: "a3",
            _id: {_id: "61606e609"}
        }
    ]
}

Thank :)

Upvotes: 1

Views: 1762

Answers (2)

piouson
piouson

Reputation: 4565

I was able to workaround the issue by reordering in onDragUpdate instead, this means state update happens as you drag, then I check in onDragEnd if reordering is still needed..

const [state, setState] = useState([]) // list of items
const dragSourceRef = useRef<number>()

const handleDragUpdate = ({ destination, source }: DragUpdate): void => {
  if (!destination) return
  setState(reorder(state, dragSourceRef.current ?? source.index, destination.index)
  dragSourceRef.current = destination.index
}

const handleDragEnd = ({ destination, source }: DropResult): void => {
  const lastSource = dragSourceRef.current
  dragSourceRef.current = undefined
  if (!destination || lastSource === destination.index) return
  setState(reorder(state, dragSourceRef.current ?? source.index, destination.index)
}

// ...
<DragDropContext onDragEnd={handleDragEnd} onDragUpdate={handleDragUpdate}>
  {/*...*/}
</DragDropContext>

Would be great to find a fix that don't require onDragUpdate or such frequent updates..

Upvotes: 0

Maxim Buz
Maxim Buz

Reputation: 31

It has probably todo with your getLists() call in the useEffect hook.

Is there an async function behind it? Do you get your lists from a server? If so, I suppose that useEffect is firing twice (once on drag end and once when it gets back the data from the backend or whatever getLists is doing), which leads to the flickering.

It would probably help to know what exactly getLists is.

Upvotes: 2

Related Questions