ReactJS and d3 state update

Viewed 153

i am still a beginner in React and i've come upon a problem which i don't know how to resolve, i have a parent component which renders a polygon on svg, based on points array

const Zone = ({ zone }) => {
  const [points, setPoints] = useState(zone.points);

  const updatePoint = (startPoint, endPoint) => {
    if (!comparePoints(startPoint, endPoint)) {
      setPoints(
        points.map((point) =>
          comparePoints(point, startPoint) ? endPoint : point
        )
      );
    }
  };

  const comparePoints = (point1, point2) => {
    if (point1.x === point2.x && point1.y === point2.y) {
      return true;
    } else {
      return false;
    }
  };

  const renderZone = () => {
    if (points) {
      return (
        <g className='polygon' transform={"translate(0,0)"}>
          <polygon points={getPointsString(points)} />
          {points.map((point) => (
            <Point
              key={getReactKey("point")}
              x={point.x}
              y={point.y}
              r={4}
              updatePoint={updatePoint}
            />
          ))}
        </g>
      );
    }
    return null;
  };

  return <>{renderZone()}</>;
};

and a Point component which represents a single point on svg canvas

const Point = ({ x, y, r, snapRange, resolution, isEditing, updatePoint }) => {
  const [position, setPosition] = useState({
    x: x,
    y: y
  });
  const pointRef = useRef(null);

  //Altering point coordinates based on drag
  const dragPoint = (event) => {
    if (!isEditing) return;
    let gridX = 0;
    let gridY = 0;

    const _x = event.x;
    const _y = event.y;

    const modX = _x % resolution;
    const modY = _y % resolution;

    const closeToHorizontal =
      modY <= snapRange || modY >= resolution - snapRange;
    const closeToVertical = modX <= snapRange || modX >= resolution - snapRange;

    if (closeToHorizontal & closeToVertical) {
      gridY = Math.round(_y / resolution) * resolution;
      gridX = Math.round(_x / resolution) * resolution;
    } else if (closeToHorizontal) {
      gridY = Math.round(_y / resolution) * resolution;
    } else if (closeToVertical) {
      gridX = Math.round(_x / resolution) * resolution;
    }

    setPosition({
      x: gridX === 0 ? _x : gridX,
      y: gridY === 0 ? _y : gridY
    });
  };

  // add drag handlers on mount
  useEffect(() => {
    const point = pointRef.current;
    // add event listeners on component mount if in editing mode
    if (isEditing) {
      const dragBehavior = d3.drag().on("drag", dragPoint);
      d3.select(point).call(dragBehavior);
    }
    // remove listener on component unmount or leaving editing mode
    return () => {
      d3.select(point).on("mousemove.drag", null);
    };
  });

  useEffect(() => {
    updatePoint({ x, y }, position);
  });

  return (
    <circle
      ref={pointRef}
      cx={position.x}
      cy={position.y}
      r={r}
      className={isEditing ? "poly-point" : "hidden"}
    />
  );
};

draggin is working fine but i want to update the new position of a point in points array of a Zone component to do so i call

useEffect(() => {
    updatePoint({ x, y }, position);
  });

so on each position state change it updates points array in Zone component, after trying dragging a circle i get the error:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

which points on this method in Point component:

setPosition({
      x: gridX === 0 ? _x : gridX,
      y: gridY === 0 ? _y : gridY
});

my guess is that after rerendering it tries to use setPosition method which points on unmounted component, any clues how to make it right?

0 Answers
Related