ClickAwayListener 组件不适用于 DragDropContext

ClickAwayListener component not working with DragDropContext

我使用 Button 制作了一个下拉菜单,使用 Material UI 组件制作了一个 Popper 下拉菜单,您可以在其中单击按钮并获得可供选择的主题列表。
要使 popper 消失,我们可以再次单击按钮或使用 <ClickAwayListener> 组件来侦听单击事件并关闭 Popper

现在我必须使列表具有拖放功能所以我使用 react-beautiful-dnd npm 包。
但是 <ClickAwayListener> 没有当我包含 <DragDropContext><Droppable><Draggable> 组件时似乎不起作用。

谁能帮我解决这个问题?

这是没有拖放功能的代码。 CodeSandbox link https://codesandbox.io/s/gallant-newton-mfmhd?file=/demo.js

const subjectsFromBackend = [
  { name: "Physics", selected: false },
  { name: "Chemistry", selected: false },
  { name: "Biology", selected: false },
  { name: "Mathematics", selected: false },
  { name: "Computer Science", selected: false },
];

const useStyles = makeStyles((theme) => ({
  root: {
    display: "flex"
  },
  paper: {
    marginRight: theme.spacing(2)
  }
}));

export default function MenuListComposition() {
  const classes = useStyles();
  const [open, setOpen] = React.useState(false);
  const anchorRef = React.useRef(null);

  const [subjects, setSubjects] = React.useState(subjectsFromBackend);

  const handleToggle = () => {
    setOpen(!open);
  };

  const handleClose = () => {
    setOpen(false);
  };

  const ColumnItem = ({ subjectName, selected }) => {
    return (
      <>
        <Grid container>
          <Grid item>
            <Checkbox checked={selected} />
          </Grid>
          <Grid item>{subjectName}</Grid>
        </Grid>
      </>
    );
  };

  return (
    <div className={classes.root}>
      <div>
        <Button
          ref={anchorRef}
          onClick={handleToggle}
          style={{ width: 385, justifyContent: "left", textTransform: "none" }}
        >
          {`Subjects Selected`}
        </Button>
        <Popper
          open={open}
          anchorEl={anchorRef.current}
          role={undefined}
          transition
          disablePortal
        >
          {({ TransitionProps, placement }) => (
            <Grow
              {...TransitionProps}
              style={{
                transformOrigin:
                  placement === "bottom" ? "center top" : "center bottom"
              }}
            >
                <Paper style={{ maxHeight: 200, overflow: "auto", width: 385 }}>
                  <ClickAwayListener onClickAway={handleClose}>
                          <List>
                            {subjects.map((col, index) => (
                              <ListItem>
                                <ColumnItem
                                  subjectName={col.name}
                                  selected={col.selected}
                                />
                              </ListItem>
                            ))}
                          </List>
                  </ClickAwayListener>
                </Paper>
            </Grow>
          )}
        </Popper>
      </div>
    </div>
  );
}



这是使用拖放的相同代码。 CodeSandbox Link https://codesandbox.io/s/material-demo-forked-ertti

const subjectsFromBackend = [
  { name: "Physics", selected: false },
  { name: "Chemistry", selected: false },
  { name: "Biology", selected: false },
  { name: "Mathematics", selected: false },
  { name: "Computer Science", selected: false },
];

const useStyles = makeStyles((theme) => ({
  root: {
    display: "flex"
  },
  paper: {
    marginRight: theme.spacing(2)
  }
}));

export default function MenuListComposition() {
  const classes = useStyles();
  const [open, setOpen] = React.useState(false);
  const anchorRef = React.useRef(null);

  const [subjects, setSubjects] = React.useState(subjectsFromBackend);

  const handleToggle = () => {
    setOpen(!open);
  };

  const handleClose = () => {
    setOpen(false);
  };

  const ColumnItem = ({ subjectName, selected }) => {
    return (
      <>
        <Grid container>
          <Grid item>
            <Checkbox checked={selected} />
          </Grid>
          <Grid item>{subjectName}</Grid>
        </Grid>
      </>
    );
  };

  const onDragEnd = (result, subjects, setSubjects) => {
    const { source, destination } = result;
    if (!destination) return;
    if (source.droppableId !== destination.droppableId) return;

    const copiedItems = [...subjects];
    const [removed] = copiedItems.splice(source.index, 1);
    copiedItems.splice(destination.index, 0, removed);
    setSubjects(copiedItems);
  };

  return (
    <div className={classes.root}>
      <div>
        <Button
          ref={anchorRef}
          onClick={handleToggle}
          style={{ width: 385, justifyContent: "left", textTransform: "none" }}
        >
          {`Subjects Selected`}
        </Button>
        <Popper
          open={open}
          anchorEl={anchorRef.current}
          role={undefined}
          transition
          disablePortal
        >
          {({ TransitionProps, placement }) => (
            <Grow
              {...TransitionProps}
              style={{
                transformOrigin:
                  placement === "bottom" ? "center top" : "center bottom"
              }}
            >
              <DragDropContext
                onDragEnd={(res) => onDragEnd(res, subjects, setSubjects)}
              >
                <Paper style={{ maxHeight: 200, overflow: "auto", width: 385 }}>
                  <ClickAwayListener onClickAway={handleClose}>
                    <Droppable droppableId={"subjectsColumn"}>
                      {(provided, snapshot) => (
                        <div
                          {...provided.droppableProps}
                          ref={provided.innerRef}
                        >
                          <List>
                            {subjects.map((col, index) => (
                              <Draggable
                                key={col.name}
                                draggableId={col.name}
                                index={index}
                              >
                                {(provided, snapshot) => (
                                  <div
                                    ref={provided.innerRef}
                                    {...provided.draggableProps}
                                    {...provided.dragHandleProps}
                                  >
                                    <ListItem>
                                      <ColumnItem
                                        subjectName={col.name}
                                        selected={col.selected}
                                      />
                                    </ListItem>
                                  </div>
                                )}
                              </Draggable>
                            ))}
                          </List>
                          {provided.placeholder}
                        </div>
                      )}
                    </Droppable>
                  </ClickAwayListener>
                </Paper>
              </DragDropContext>
            </Grow>
          )}
        </Popper>
      </div>
    </div>
  );
}

使用拖放时,您需要将 ClickAwayListener 放在顶部。

return (
    <ClickAwayListener
      onClickAway={() => {
        console.log("i got called");
        handleClose();
      }}
    >
     ..... 
    </ClickAwayListener>

这是工作代码和框 - https://codesandbox.io/s/material-demo-forked-h1o1s?file=/demo.js:4877-4897

我发现 ClickAwayListener 的子组件应该包裹在 div 周围,这样才能正确触发点击事件。