如何在 ReactJS 中制作可重复使用的下拉过滤器,它是 DRY 并且可以在同一组件中使用两次

How to make a reusable dropdown filter in ReactJS which is DRY and can be used twice in the same component

如您所见,我使用了两个下拉过滤器 select 选项,该选项是使用 divs 自定义的,而不是 select 选项。为了重用这个自定义的 select 选项,我正在寻找一种可重用的方式,这样我就可以重用下拉过滤器一次,而不是复制非 DRY 的 de 代码。如何创建两个这样的下拉过滤器?

最重要的是我能够将 selected 值分开。

interface TableComponentProps {
    columns: Array<any>;
}

export const TableComponent: FC<TableComponentProps> = ({ columns}) => {
    const [columnShow, setColumnShow] = useState<string>("");
    const [openDropdown, setOpenDropdown] = useState(false);
    const wrapperRef = useRef(null);
    const data = useMemo(() => TableContent, []);

    const useOutsideAlerter = (ref: any) => {
        useEffect(() => {
            const handleClickOutside = (event: any) => {
                if (ref.current && !ref.current.contains(event.target)) {
                    setOpenDropdown(false)
                }
            }

            document.addEventListener("mousedown", handleClickOutside);
            return () => {
                document.removeEventListener("mousedown", handleClickOutside);
            };
        }, [ref]);
    }

    useOutsideAlerter(wrapperRef)

    const showColumn = () => {
        let newArray: any = [];

        console.log(newArray)
    }
        
    const dropdownFilter = (selectedColumn: string) => {
        setColumnShow(selectedColumn);
        setOpenDropdown(false);
        showColumn()
    }

    const toggleDropdownOpen = () => setOpenDropdown(!openDropdown);

    return (
        <div>
            <TableFilter>
                <TableFilterBlock ref={wrapperRef}>
                    <TableFilterInput onClick={() => toggleDropdownOpen()}>
                    {columnShow.length > 0 ? columnShow : "Select"}</TableFilterInput>
                    <TableFilterDropdown toggleDropdown={openDropdown}>
                        {columns.slice(1).map((item, index) => (
                            <TableDropdownList key={index}>
                                <div onClick={() => dropdownFilter(item.id)}>
                                    {item.id}
                                </div>
                            </TableDropdownList>
                        ))}
                    </TableFilterDropdown>
                </TableFilterBlock>
            </TableFilter>

            <TableFilter>
                <TableFilterBlock ref={wrapperRef}>
                    <TableFilterInput onClick={() => toggleDropdownOpen()}>
                    {columnShow.length > 0 ? columnShow : "Select"}</TableFilterInput>
                    <TableFilterDropdown toggleDropdown={openDropdown}>
                        {columns.slice(1).map((item, index) => (
                            <TableDropdownList key={index}>
                                <div onClick={() => dropdownFilter(item.id)}>
                                    {item.id}
                                </div>
                            </TableDropdownList>
                        ))}
                    </TableFilterDropdown>
                </TableFilterBlock>
            </TableFilter>
            <Table columns={columns} data={data} hiddenColumns={columnShow}/>
        </div>
    )
}

在 React 中,如果你想重用代码片段,你需要使用组件,特别是所谓的提取组件。 React Components and Props > Extracting Components 该页面解释了我在这里为您所做的以下内容。

首先您需要为下拉菜单创建一个组件,并评估哪些代码对于下拉菜单+按钮是唯一的。

const InlineComponent = ({
  columns,
  showColumn,
  onSelect
}) => {
  // move the dropdown logic here
  const ref = useRef();
  const [name, setName] = useState<string>("");
  const [open, setOpen] = useState(false);
  const openDropdown = () => setOpen(true);

  const itemClick = (selectedColumn: string) => {
    setName(selectedColumn);
    setOpen(false);
    showColumn();
    onSelect(selectedColumn);
  };

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (ref.current && !ref.current.contains(event.target)) {
        setOpen(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [ref]);

  return (
    <TableFilter>
      <TableFilterBlock ref={ref}>
        <TableFilterInput onClick={() => openDropdown()}>
          {name.length > 0 ? name : "Select"}
        </TableFilterInput>
        <TableFilterDropdown toggleDropdown={open}>
          {columns.slice(1).map((item, index) => (
            <TableDropdownList key={index}>
              <div onClick={() => itemClick(item.id)}>{item.id}</div>
            </TableDropdownList>
          ))}
        </TableFilterDropdown>
      </TableFilterBlock>
    </TableFilter>
  );
};

然后从原始位置引用它并传入任何共享道具。

export const TableComponent: FC<TableComponentProps> = ({ columns }) => {
  const [hiddenCol, setHiddenCol] = useState<string>("");
  const data = useMemo(() => TableContent, []);

  const showColumn = () => {
    let newArray: any = [];

    console.log(newArray);
  };

  return (
    <div>
      {/* add the newly created components here */}
      <InlineComponent
        columns={columns}
        showColumn={showColumn}
        onSelect={setHiddenCol}
      />
      <InlineComponent
        columns={columns}
        showColumn={showColumn}
        onSelect={setHiddenCol}
      />

      <Table columns={columns} data={data} hiddenColumns={hiddenCol} />
    </div>
  );
};

公平警告我没有测试 运行 此代码,您应该自己评估哪些部分需要保留在何处。