Maintaining state with react JS Drag and Drop (DnD) and redux

I am currently working on a form builder which is part of the website builder for thephpcode.com CodeIgniter website generator. So the requirement was to allow users to create dynamic pages by dragging and dropping elements as part of the data model and also support dragging and dropping other building block elements such as column separator, line breaker and heading.

The implementation was easy but found an issue when using the redux. The redux state always reset to the initial one so I always lost the items previously lost when dropping new item to the form. This post is to help anyone experiencing the similar issue.

What is the Issue?

The issue is we need to let the useDrop hook which properties is getting changed when an something is dropped in the bounded section. Then the state will be kept up to date and will be used when the next item is dropped, otherwise all the time the initial state is used.

The solution

The solution is to pass array of properties which needs to be refreshed after each drop. So the DnD component will keep track of up to date state. The example is shown below.

import React from "react";
import { useDrop } from "react-dnd";

import ItemTypes from "./ItemTypes";

function Form(props) {
  const addItem = (item) => {
    props.addItems(item);
  };

  const [{ canDrop, isOver }, drop] = useDrop(
    () => ({
      accept: ItemTypes.CARD,
      drop: (item, monitor) => addItem(item),
      collect: (monitor) => ({
        isOver: monitor.isOver(),
        canDrop: monitor.canDrop(),
      }),
    }),
    [props.items]
  );

  return (
    <div className="card card-default shadow-sm mt-3">
      <div className="card-body react-form-builder-form">
        {props.items.map((item, index) => {
          return (
            <div className="item" key={index}>
              {item.name}
            </div>
          );
        })}
        <div ref={drop}>
          {/* {canDrop ? "Release to drop" : "Drag a box here"} */}
          {isOver && canDrop && (
            <div className="form-place-holder">
              <div>Release to Drop</div>
            </div>
          )}
          {!isOver && (
            <div className="form-place-holder">
              <div>Dropzone</div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

export default Form;

Here the items is updated by the parent component when something is dropped in to the form. By providing the optional parameter shown in bold the state is kept up to date by the React DnD and used when next element is dropped. So no more issues with initial state being used.

References

I referred some stack overflow answer to arrive at correct solutions. Here are the references used.

https://stackoverflow.com/questions/70566291/react-dnd-usedrop-is-not-using-the-current-state-when-the-method-is-called

Leave a Comment