如何将React组件传递给组件并在组件内部作为组件使用?

问题描述 投票:0回答:1

我有一个

Button
组件,允许一些道具(如
variant
active
size
等)更改其样式。

我想要的是将 Button 组件传递给另一个组件并在那里将其用作普通组件。

function Header() {
  return (
    <div>
      <Button.Link href="/">Log in</Button.Link>
      <Button.Link href="/">Sign up</Button.Link>
      <Dropdown
        Trigger={ 
          <Button variant="outline" size="icon">
            <MenuIcon className="size-4" />
          </Button>
        }
      >
        <div className="absolute right-0 top-8 h-72 w-64 bg-red-200">Hello</div>
      </Dropdown>
    </div>
  );
}

export default Header;

如您所见,我将

Button
组件传递给
Dropdown
组件。

现在在下拉菜单中,我想使用它,如下所示:(用于解释的虚拟代码)

interface DropdownProps {
  Trigger: any;
  children: React.ReactNode;
}

function Dropdown({ Trigger, children }: DropdownProps) {
  const [isOpen, setIsOpen] = useState(false);

  const dropdownRef = useRef<HTMLDivElement>(null);

  const toggleDropdown = () => {
    setIsOpen((current) => !current);
  };

  useClickOutside(dropdownRef, () => setIsOpen(false));

  return (
    <div ref={dropdownRef} className="relative">
      {<Trigger onClick={toggleDropdown} active={isOpen} />} <-- like this
      {isOpen && children}
    </div>
  );
}

export default Dropdown;

如何实现这个功能?


我做了什么:

我找到了一个使用 React 的解决方案

cloneElement
但对此并不满意(感觉有更好的方法)。

interface DropdownProps {
  trigger: React.ReactElement<{
    onClick: () => void;
    active: boolean;
  }>;
  children: React.ReactNode;
}

function Dropdown({ trigger, children }: DropdownProps) {
  const [isOpen, setIsOpen] = useState(false);

  const dropdownRef = useRef<HTMLDivElement>(null);

  const toggleDropdown = () => {
    setIsOpen((current) => !current);
  };

  useClickOutside(dropdownRef, () => setIsOpen(false));

  return (
    <div ref={dropdownRef} className="relative">
      {cloneElement(trigger, { onClick: toggleDropdown, active: isOpen })}
      {isOpen && children}
    </div>
  );
}

export default Dropdown;

还有更好的吗?

javascript reactjs jsx
1个回答
0
投票

管理 props 并确保更好的类型安全性的更好方法是将 Trigger 定义为直接从 Dropdown 接收必要的 props 和事件的函数组件。这种方法避免了 React.cloneElement,使代码更干净并提供更好的类型安全性。

以下是如何使用 Trigger 函数组件来实现它,该组件从 Dropdown 接收 onClick 和 active 属性。

import React, { useState, useRef } from 'react';

interface DropdownProps {
  Trigger: (props: { onClick: () => void; active: boolean }) => JSX.Element;
  children: React.ReactNode;
}

function Dropdown({ Trigger, children }: DropdownProps) {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef<HTMLDivElement>(null);

  const toggleDropdown = () => {
    setIsOpen((current) => !current);
  };

  useClickOutside(dropdownRef, () => setIsOpen(false));

  return (
    <div ref={dropdownRef} className="relative">
      <Trigger onClick={toggleDropdown} active={isOpen} />
      {isOpen && children}
    </div>
  );
}

export default Dropdown;

现在在 Header 中,您可以将 Trigger 作为函数传递,并将其与 Button 组件一起使用:


function Header() {
  return (
    <div>
      <Button.Link href="/">Log in</Button.Link>
      <Button.Link href="/">Sign up</Button.Link>
      <Dropdown
        Trigger={({ onClick, active }) => (
          <Button variant="outline" size="icon" onClick={onClick} active={active}>
            <MenuIcon className="size-4" />
          </Button>
        )}
      >
        <div className="absolute right-0 top-8 h-72 w-64 bg-red-200">Hello</div>
      </Dropdown>
    </div>
  );
}

希望对你有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.