atlassian / react-beautiful-dnd

Beautiful and accessible drag and drop for lists with React
https://react-beautiful-dnd.netlify.app
Other
33.32k stars 2.55k forks source link

Unable to find drag handle with id "ID_HERE" as no handle with a matching id was found #2210

Closed EdmundsEcho closed 3 years ago

EdmundsEcho commented 3 years ago

Description

Console reports the error for all of my handles that pass the dragHandleProps to the child components (cascading the render-prop pattern to the next component).

Expected behavior

No error/warning message.

Actual behavior

Displays the errors/warnings. It may be a reason my dnd experience is a "bit wonky", things sometimes work as expected as I play with minimum padding etc.

Steps to reproduce

This is the code that seems to be problematic:

const Dragger = (props) => {
  const { config, children /* mutable */ } = props;
  const classes = useStyles();
  console.log(`Dragger: ${config.id}`);
  console.dir(children);
  return (
    <Draggable
      key={`dnd-draggable-${config.id}`}
      draggableId={config.id}
      index={config.index}
      isDragDisabled={config.isDragDisabled}
    >
      {(provided, snapshot) => {
        return (
          <div
            ref={provided.innerRef}
            {...provided.draggableProps}
            className={clsx('Workbench-Dragger', classes.dragger, {
              [classes.activeDragging]: snapshot.isDragging,
            })}
            style={getItemStyle(provided.draggableProps.style)}
          >
            {/* children = Node */}
            {children(provided.dragHandleProps)}  // <<<<< Here
          </div>
        );
      }}
    </Draggable>
  );
};

The above children are functions that expect the dragHandleProps:

function renderChildren(childIds, type, areChildrendDragDisabled, height) {
  return childIds.map((childId, index) => (
    <Dragger
      key={`dragger-${childId}`}
      config={{
        id: `${childId}`,
        index,
        isDragDisabled: areChildrendDragDisabled,
        height: height + 1, // informational
      }}
      moveOrCopy={type === NODE_TYPES.PALETTE ? 'COPY' : 'MOVE'}
    >
      {(dragHandleProps) => (
        <Node
          key={`node-${childId}`}
          id={childId}
          type={type}
          dragHandleProps={dragHandleProps}
        />
      )}
    </Dragger>
  ));
}

The Node component renders a series of components. The dragHandleProps are "drilled-down" to the component I ultimately use as the dnd handle. Perhaps only as an FYI, I show here the component nests a DropZone which in turn render another Dragger. The nesting occurs twice.

          <EtlUnitGroup
            context={paletteOrCanvas}
            config={{ type: 'empty' }}
            dragHandleProps={dragHandleProps}
            className={clsx('MuiWorkbench-Group')}
          >
            <DropZone
              key={`drop-zone-${dropZoneAdjustedState[0]}`}
              config={{
                id: `${dropZoneAdjustedState[0]}`,
                height: dropZoneAdjustedState[1],
              }}
              direction={direction}
              isDropDisabled={isDropDisabled}
            >
              {/* Draggable children */}
              {childIds?.length &&
                renderChildren(
                  childIds,
                  paletteOrCanvas,
                  areChildrendDragDisabled,
                  height,
                )}
            </DropZone>
          </EtlUnitGroup>

// and the component to which the props are drilled-down to
const EtlUnitGroup = ({ context, config, dragHandleProps, children }) => {
  return (
    <EtlUnitGroupBase
      type={context === 'palette' ? 'shell' : config.type}
      version={1}
      dragHandleProps={dragHandleProps}
    >
      {children}
    </EtlUnitGroupBase>
  );
};

// and another
function EtlUnitGroupBase({
  type,
  version,
  config,
  dragHandleProps,
  children,
}) {
  const classes = useStyles();
  const { displayType } = useContext(EtlUnitGroupContext);
  /* eslint-disable react/jsx-props-no-spreading */
  return (
    <Box className={clsx('EtlUnit-GroupBase', classes.base, type)}>
      <EtlUnitGroupProvider>
        {/* Header */}
        <CardContent className={clsx('EtlUnit-GroupBase')}>
          {type === 'shell' ? null : (
            <Header
              className={clsx('header')}
              type={type}
              version={version}
              dragHandleProps={dragHandleProps}
            />
          )}
        </CardContent>
        {/* Group semantic */}
        <Grid container spacing={0}>
          <Grid item xs>
            <Collapse in={displayType !== 'empty'} timeout='auto' unmountOnExit>
              <div className={classes.paper}>
                Placeholder for what a group means
              </div>
            </Collapse>
          </Grid>
        </Grid>
        {/* EtlUnits */}
        <Grid container spacing={0}>
          <Grid item xs>
            {children}
          </Grid>
        </Grid>
        {/* Footer */}
        <Grid container spacing={0}>
          <Grid item xs>
            {type === 'shell' ? null : (
              <Footer type={type} showFab={type === 'empty'} />
            )}
          </Grid>
        </Grid>
      </EtlUnitGroupProvider>
    </Box>
  );
}

// and now the final destination
function Header({ type, version, dragHandleProps }) {
  const classes = useHeaderStyles();
  //
  let titleProps = {};
  switch (type) {
    case 'subjectUniverse':
      titleProps = { Icon: PersonIcon, text: 'Subject Universe' };
      break;
    default:
      titleProps = { Icon: Empty, text: '' };
  }
  /* eslint-disable react/jsx-props-no-spreading */
  return (
    <div
      className={clsx('EtlUnit-GroupCardHeader', classes.root)}
      {...dragHandleProps}       // <<<<<<< Used here
    >
      <Title {...titleProps} />
      <Tools version={version} />
    </div>
  );
}

I have confirmed that the props "make it" to the final destination. Here is a partial view of that confirmation. Screen Shot 2021-05-05 at 3 01 38 PM

Here is the dnd warning for that same dragger: Screen Shot 2021-05-05 at 3 04 10 PM

Suggested solution?

Is there a limit to "how far" dnd searches for the handle id? Is my version of react supported 17.0.1 with dnd 13.1.0?

What browser are you using?

Firefox dev

Thank you in advance for any guidance... bonus: also vis a vis best-practices for how to achieve a more consistent dnd experience when nesting as I have.

EdmundsEcho commented 3 years ago

Resolved

A version of one of the components (a "shell" version), did not render the handle props. Once I did so using <div {... dragHandleProps} />, the warnings were resolved.