【问题标题】:What is the React way of inserting an icon into another component?将图标插入另一个组件的 React 方式是什么?
【发布时间】:2021-04-16 12:34:07
【问题描述】:

我正在尝试创建一个 WithIcon 包装组件,它将一个子(图标)插入到一个包装组件中。

假设我有一个按钮:

<Button>Add item</Button>

我想创建一个 WithIcon 组件,它会像这样使用:

<WithIcon i="plus"><Button>Add item</Button></WithIcon>

我最终想要实现的是:

<Button className="with-icon"><i className="me-2 bi bi-{icon}"></i>Add item</Button>

注意按钮正文中添加的类名和标签。

我试图弄清楚 WithIcon 组件的代码应该是什么样子。实现这个结果的 React 方式是什么?

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    最难的部分是使用WithIcon 的规则我们只有一个吗? 我们只会在最左边吗?类似的东西。

    但是,如果我们以您为榜样。对于WithIcon我们可以相对写这样的东西

    const WithIcon = ({ i, children }) => {
      return React.Children.map(children, (child) => {
        return (
          <>
            <i className={`me-2 bi bi-${i}`}></i>
            {React.cloneElement(child, { className: "with-icon" })}
          </>
        );
      });
    };
    

    那么我们就可以随心所欲地使用它了

    <WithIcon i="plus"><Button>Add item</Button></WithIcon>
    

    我们所做的只是循环遍历子节点,这些子节点的反应是您放入其中的任何嵌套 jsx(在我们的例子中为按钮)

    你可以在这里找到我的小提琴:https://codesandbox.io/s/react-font-awesome-forked-321tz?file=/src/index.js

    更新 所以我之前的回答并没有完全达到我们想要的最终结果。遗嘱必须是主要父母

    这个想法还是和之前的一样,但是这里我们在推断我们在 WithIcon 中传递的组件的类型这也增加了当我们在 WithIcon 中传递嵌套组件时的安全措施

    const WithIcon = ({ i, children }) => {
      return React.Children.map(children, (child) => {
        const MyType = child.type; // So we can get the Button
        return (
          <MyType className="with-icon">
            <i className={`me-2 bi bi-${i}`}></i>
            {(React.cloneElement(child, {}), [child.props.children])}
          </MyType>
        );
      });
    };
    

    我想我要睡觉了,我稍后会更新其余的解释。 在这里看小提琴: https://codesandbox.io/s/react-font-awesome-forked-y43fx?file=/src/components/WithIcon.js

    请注意,此代码不会保留传递组件的其他道具,但您可以通过在 MyComponent 处添加 {...child.props} 来相对添加它,这只是(反射之类的?)推断组件。

    当然还有其他选项,例如 HOC Enhancers 来执行此操作,但这会增加您如何声明组件 api 的复杂性。所以选择最适合你的朋友

    【讨论】:

    • 感谢您的回答,但这只是在按钮之前添加了 标记。我希望将 标记插入到 Button 中,并将类名“with-icon”添加到 Button。
    • 啊,正确的将类添加到按钮主体。是的,很快就会更新答案。 :)
    • 哦,对不起,我在半醒模式下回答这个问题。我注意到您想要的最终结果是
    • @janhink 我更新了答案,对不起伙计
    • 感谢您的更新,这正是我想要的!
    【解决方案2】:

    也许尝试使用更高阶的组件?

    const withIcon = (icon, Component) => ({children, ...props}) => {
        return (
            <Component className="with-icon" {...props}>
                <i className=`me-2 bi bi-${icon}` /> 
                {children}
            </Component>
        );
    }
    

    那么用法就是

    const ButtonWithIcon = withIcon("your-icon", Button);
    
    <ButtonWithIcon>Add Item</ButtonWithIcon>
    

    根据我对 react 的经验,它通常归结为使用组件内部的属性,例如此处 (https://material-ui.com/api/button/),或者像我描述的那样使用更高阶的组件。

    【讨论】:

    • 谢谢,这绝对是一种方法,但我特意要求一个封装任何其他组件的 组件。因此,我将@keysl 的答案标记为已接受。
    【解决方案3】:

    React 中使用了两种常见的模式来实现这种组合:

    Higher-Order Components

    首先为您的按钮定义一个组件:

    const Button = ({ className, children }) => (
      <button className={className}>{children}</button>
    );
    

    那么高阶组件可以这样实现:

    const withIcon = (Component) => ({ i, className = '', children, ...props }) => (
      <Component {...props} className={`${className} with-icon`}>
        <i className={`me-2 bi bi-${i}`} />
        {children}
      </Component>
    );
    

    用法:

    const ButtonWithIcon = withIcon(Button);
    
    <ButtonWithIcon i="plus">Add Item</ButtonWithIcon>
    

    Context

    首先定义图标的上下文提供者:

    import { createContext } from 'react';
    
    const Icon = createContext('');
    
    const IconProvider = ({ i, children }) => (
      <Icon.Provider value={i}>{children}</Icon.Provider>
    );
    

    然后是你的组件:

    import { useContext } from 'react';
    
    const Button = ({ className = '', children }) => {
      const i = useContext(Icon);
    
      if (i) {
        className += ' with-icon';
        children = (
          <>
            <i className={`me-2 bi bi-${i}`} />
            {children}
          </>
        );
      }
    
      return (
        <button className={className}>{children}</button>
      );
    };
    

    用法:

    <IconProvider i="plus"><Button>Add Item</Button></IconProvider>
    

    【讨论】:

    • 谢谢你,Patrick,这两种方法也都有效!但是,我特别要求一个封装任何其他组件的 组件。因此,我将@keysl 的答案标记为已接受。
    • @janhink 没关系。您要求“反应方式”。我认为这意味着使用现有的、可组合的模式来满足您所需的功能要求,而不是您要求的确切文字语法。顺便说一句,在他们的回答中,{(React.cloneElement(child, {}), [child.props.children])} 可以简化为{child.props.children}
    猜你喜欢
    • 1970-01-01
    • 2015-01-30
    • 2011-08-18
    • 2018-06-12
    • 2021-10-07
    • 1970-01-01
    • 2022-10-04
    • 2019-02-26
    • 1970-01-01
    相关资源
    最近更新 更多