【问题标题】:How to create a nested react componet?如何创建嵌套的反应组件?
【发布时间】:2022-01-22 00:02:49
【问题描述】:

我有一个下拉组件。这将有 2 个道具。

  1. 触发器(将触发下拉菜单)
  2. list(下拉列表内容)

这是我的组件:

import { useLayer } from "react-laag";
import { ReactElement, useState } from "react";
import classNames from 'classnames';

interface Props {
    trigger?: string | ReactElement
    children?: ReactElement | Array<ReactElement>
    className?: string
}

const Dropdown = ({ trigger, children, className }: Props) => {
    const [isOpen, setOpen] = useState(false);

    // helper function to close the menu
    function close() {
        setOpen(false);
    }

    const { renderLayer, triggerProps, layerProps } = useLayer({
        isOpen,
        onOutsideClick: close, // close the menu when the user clicks outside
        onDisappear: close, // close the menu when the menu gets scrolled out of sight
        overflowContainer: true, // keep the menu positioned inside the container
        auto: true, // automatically find the best placement
        placement: "bottom-end", // we prefer to place the menu "top-end"
        triggerOffset: 12, // keep some distance to the trigger
        containerOffset: 16, // give the menu some room to breath relative to the container
        arrowOffset: 16 // let the arrow have some room to breath also
    });

    // Again, we're using framer-motion for the transition effect
    return (
        <div className='dropdown' >
            <button className='dropdown-trigger' {...triggerProps} onClick={() => setOpen(!isOpen)}>
                {* I want to render trigger here *}
            </button>
            {isOpen &&
                renderLayer(
                    <div className={classNames(['dropdown-list', className])} {...layerProps}>
                        {* I want to render list here *}
                    </div>
                )}
        </div>
    );
}

export default Dropdown

这里我使用react-lagg 作为下拉菜单。

我想这样使用这个组件:

<Dropdown>
   <Dropdown.Trigger>
    <div>Some JSX content here</div>
   </Dropdown.Trigger>
   <Dropdown.List>
    <div>Some JSX content here</div>
   </Dropdown.List>
</Dropdown>

如何在 react 上实现这种模式?

【问题讨论】:

    标签: javascript reactjs typescript


    【解决方案1】:

    基本上,如果您想以这种方式使用该组件,请试试这个。 **我会用JS写的

    const Dropdown = ({ children }) => {
      return <div className="dropdown">{children}</div>;
    };
    
    const Trigger = (triggerProps) => (
      <button className="dropdown-trigger" {...triggerProps}>
        {triggerProps.children}
      </button>
    );
    const List = (listProps) => (
      <div className="dropdown-list" {...listProps}>
        {listProps.children}
      </div>
    );
    
    Dropdown.Trigger = Trigger;
    Dropdown.List = List;
    
    export default Dropdown;

    在您的情况下,Dropdown.Trigger 正在从Dropdown 触发状态。我不确定这是不是最佳做法,你可以这样写

    import React, { useState, cloneElement } from 'react';
    import classNames from 'classnames';
    
    const Trigger = (triggerProps) => (
      <button className="dropdown-trigger" {...triggerProps}>
        {triggerProps.children}
      </button>
    );
    const List = ({ className, ...listProps }) => (
      <div className={classNames(['dropdown-list', className])} {...listProps}>
        {listProps.children}
      </div>
    );
    
    const Dropdown = ({ trigger, children, className }) => {
      const [isOpen, setOpen] = useState(false);
    
      return (
        <div className="dropdown">
          {cloneElement(
            children.find(({ type }) => type === Trigger),
            {
              onClick: () => setOpen(!isOpen),
            }
          )}
          {isOpen &&
            cloneElement(
              children.find(({ type }) => type === List),
              {
                className: className,
              }
            )}
        </div>
      );
    };
    
    Dropdown.Trigger = Trigger;
    Dropdown.List = List;
    
    export default Dropdown;

    会这样使用

    <Dropdown className="test">
      <Dropdown.Trigger>trigger</Dropdown.Trigger>
      <Dropdown.List>list</Dropdown.List>
    </Dropdown>

    【讨论】:

    • 问题已解决。我想用动态顺序来处理这个。有什么方法可以专门用name/type 这样的东西而不是索引来选择子组件?
    • 对,我们可以做到,我已经编辑了答案
    • 你怎么看?这是正确的方法吗?您可以查看react-bootstrap.github.io 组件。他们是怎么做到的?
    猜你喜欢
    • 2019-11-06
    • 2017-07-20
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    • 2015-09-15
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多