react-dnd / react-dnd

Drag and Drop for React

Home Page:http://react-dnd.github.io/react-dnd

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Custom Drag Layer elements don't update until AFTER I drag it.

EOMedvis opened this issue · comments

I'm new to React development. As shown below I have a draggable object with a custom drag layer used as its preview (IconPreview) because normal previews don't work on mobile. The preview takes in a string to load an image. I'm running into a problem where if I have multiple draggables on the screen at once, the image doesn't update on the first drag, and it will use the image for the last object I dragged until I drag and let go, then it updates.

I've asked Chat GPT for what the issues is, and its telling me that Custom Drag Layers don't always update immediately due to "performance issues" and that its not fixable without another package. I'm not sure if its BSing me or not. Am I doing something wrong or is this a React DnD issue?

Code:

import { CSSProperties, FC, useState, useEffect } from 'react';
import { useDrag, useDragLayer } from 'react-dnd';
import { DraggableTypes } from './DraggableTypes';
import { getEmptyImage } from "react-dnd-html5-backend";

const iconWidth: number = 48;
const iconHeight: number = 64;

const styleIconNormal: CSSProperties = 
{
  position: 'absolute',
  width: iconWidth + "px",
  height: iconHeight + "px",
  cursor: 'move',
  backgroundImage: 'linear-gradient(to top, #396BE5, #ACA9E4)', 
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  border: '2px solid white',
  borderRadius: '5px',
};

const styleIconImage: CSSProperties =
{
  position:'relative',  
  width: '32px',
  height: '32px',
}

export interface IconProps 
{
  iconData: IconType
  SetInfo(info: {title: string, desc: string, image: string, ranked: boolean, discarded: boolean}): void;  
  feels: boolean;
  SetIconToGlow(icon: string): void;
  SetClicked(clicked: boolean): void;
}

interface IconPreviewProps
{
  imageName: string;
}

//custom drag layer used for a custom preview.
const IconPreview: React.FC<IconPreviewProps> = ({ imageName }) => 
{
  const { isDragging, currentOffset } = useDragLayer((monitor) => 
  ({
    isDragging: monitor.isDragging(),
    currentOffset: monitor.getClientOffset()
  }));  

  if (!isDragging) 
  {
    return null;
  }

  return (
    <div style=
      {{
        ...styleIconNormal, 
        position: 'fixed', 
        pointerEvents: 'none', 
        zIndex: 100, 
        left: (currentOffset?.x || 0) - iconWidth/2, 
        top: (currentOffset?.y || 0) - iconHeight/2,
      }}>      
      <img src={"icon-" + imageName + ".png"} alt="Icon" style={styleIconImage}/>
    </div>
  );
};

//main code for the drop icons that is created when a card is dropped over a rating zone. Props must be listed individually for this function, for some reason, otherwise using iconData as a part of props.iconData breaks the redrag of the 
export const DropIcon: FC<IconProps> = function SetIcon({ iconData, SetInfo, feels, SetIconToGlow, SetClicked}) 
{ 
  const [{ isDragging }, drag, preview] = useDrag
  (() => 
  ({
    type: DraggableTypes.Icon,
    item: {iconData},
    collect: (monitor) => 
      ({
        isDragging: monitor.isDragging()
      }),
      previewOptions: { captureDraggingState: true,} 
    }),
    [iconData]
  );

  useEffect(() => 
  {
    preview(getEmptyImage(), { captureDraggingState: true })
  }, [iconData]);

  const opacity = isDragging ? 0.4 : 1;
  var boxShadow = '';  

  //on mouse down function. Tells Dropzones to change this cards data to glow and fills in the info box.
  function MouseDown()
  {
    if(feels)   
    {     
      SetIconToGlow(iconData.title);
      SetInfo({title: iconData.title, desc: iconData.desc, image: iconData.image, ranked: iconData.ranked, discarded: iconData.discarded});
      SetClicked(true); 
    }       
  }  

  //if in emotion ranking mode, and this card is set to glow, use the glow effect.
  if(iconData.glow && feels)
    boxShadow = '0px 0px 40px 20px #ffffff';  

  return (
    <>
    <IconPreview imageName={iconData.image}/>
    <button ref={drag} 
      onMouseDown={MouseDown} 
      style= {{ ...styleIconNormal, opacity, boxShadow, left: iconData.left + '%', top: iconData.top + '%' }}
    >
      <img src={"icon-" + iconData.image + ".png"} alt="Icon" style={styleIconImage}/>
    </button>   
    </> 
  );
};

Problem fixed. I wasn't using the userDragLayer monitor to check if the item image changed. Doing the following fixed it:

interface IconPreviewProps
{
imageName: string;
isDragging: boolean;
}

//custom drag layer used for a custom preview.
const IconPreview: FC = (props) =>
{
const {imageName, isDragging, currentOffset } = useDragLayer((monitor) =>
({
imageName: props.imageName,
isDragging: props.isDragging,
currentOffset: monitor.getClientOffset()
}));

console.log("item: " + props.imageName);

if (!isDragging)
{
return null;
}

return (
<div style=
{{
...styleIconNormal,
position: 'fixed',
pointerEvents: 'none',
zIndex: 100,
left: (currentOffset?.x || 0) - iconWidth/2,
top: (currentOffset?.y || 0) - iconHeight/2,
}}>
<img src={"icon-" + imageName + ".png"} alt="Icon" style={styleIconImage}/>

);
};