简体   繁体   中英

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

As you might see I use two dropdown filter select option which is customised with divs instead of select option. In order to reuse this customized select option, I am looking for a reusable way so I can reuse the dropdown filter one time instead of copying de code which is not DRY. How can I create two dropdown filter like that?

Most important thing is that I am able to get the selected value separated.

 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> ) }

In react if you want to reuse pieces of code, you need to use components, specifically what's known as Extracting Components. React Components and Props > Extracting Components That page explains the following that I did for you here.

Firstly you need to create a component for the dropdown, and evaluate which code is unique to the dropdown + button.

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>
  );
};

Then you reference it from the original location and pass in any shared props.

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>
  );
};

Fair warning I didn't test run this code and you should evaluate yourself which parts you need to keep where.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM