【问题标题】:How do I conditionally wrap a React component?如何有条件地包装 React 组件?
【发布时间】:2015-11-14 16:48:49
【问题描述】:

我有一个组件,有时需要呈现为<anchor>,有时需要呈现为<div>。我读到的propthis.props.url

如果存在,我需要渲染包装在<a href={this.props.url}> 中的组件。否则它只会被渲染为<div/>

可能吗?

这就是我现在正在做的事情,但觉得可以简化:

if (this.props.link) {
    return (
        <a href={this.props.link}>
            <i>
                {this.props.count}
            </i>
        </a>
    );
}

return (
    <i className={styles.Icon}>
        {this.props.count}
    </i>
);

更新:

这是最后的锁定。感谢您的提示,@Sulthan

import React, { Component, PropTypes } from 'react';
import classNames from 'classnames';

export default class CommentCount extends Component {

    static propTypes = {
        count: PropTypes.number.isRequired,
        link: PropTypes.string,
        className: PropTypes.string
    }

    render() {
        const styles = require('./CommentCount.css');
        const {link, className, count} = this.props;

        const iconClasses = classNames({
            [styles.Icon]: true,
            [className]: !link && className
        });

        const Icon = (
            <i className={iconClasses}>
                {count}
            </i>
        );

        if (link) {
            const baseClasses = classNames({
                [styles.Base]: true,
                [className]: className
            });

            return (
                <a href={link} className={baseClasses}>
                    {Icon}
                </a>
            );
        }

        return Icon;
    }
}

【问题讨论】:

  • 您也可以将const baseClasses = 移动到该if (this.props.link) 分支中。由于您使用的是 ES6,因此您也可以通过 const {link, className} = this.props; 简化一点,然后使用 linkclassName 作为局部变量。
  • 伙计,我喜欢它。越来越多地了解 ES6,它总是会提高可读性。感谢您的额外提示!
  • 什么是“最终锁定”?

标签: javascript reactjs


【解决方案1】:

只使用一个变量。

var component = (
    <i className={styles.Icon}>
       {this.props.count}
    </i>
);

if (this.props.link) {
    return (
        <a href={this.props.link} className={baseClasses}>
            {component}
        </a>
    );
}

return component;

或者,您可以使用辅助函数来呈现内容。 JSX 和其他代码一样。如果要减少重复,请使用函数和变量。

【讨论】:

  • 此代码将卸载组件并在下次渲染时重新创建它。您可以添加 key="theSameKey" 但这不会改变任何事情。 React.useMemo 不能缓存你的组件,它将被卸载。 medium.com/@cowi4030/…
  • @sytolk 不是真的。只有当您的层次结构发生变化时才会发生这种情况。这只是可以预料的。您提到的文章称其为反模式,但事实并非如此。并且那篇文章关于 react DOM diffing 也是公然错误的。
  • 是的,这是反模式 - 查看 JSX 存在两次,如果您在下一次渲染时更改 props.link,React 将卸载组件。对于这种情况,它可以接受,但如果组件很重怎么办?
  • @sytolk 通过链接包装或不包装意味着您必须更改层次结构,并且无法绕过它。这是您在设计层次结构时必须考虑的事情,但这并不意味着它是一种反模式。我认为在许多情况下它实际上是唯一可行的选择。
  • @sytolk 您提到的文章是在讨论子级的条件渲染,但在这里我们讨论的是包装器/父级。这是两个不同的东西。
【解决方案2】:

创建一个 HOC(高阶组件)来包装你的元素:

const WithLink = ({ link, className, children }) => (link ?
  <a href={link} className={className}>
    {children}
  </a>
  : children
);

return (
  <WithLink link={this.props.link} className={baseClasses}>
    <i className={styles.Icon}>
      {this.props.count}
    </i>
  </WithLink>
);

【讨论】:

  • HOC 应该慢慢消亡:P
  • 这不是 HOC。来自React Docs:“具体来说,高阶组件是一个接受一个组件并返回一个新组件的函数。”在这里,WithLink 只是一个组件。这个例子看起来好像它是在另一个组件中声明的,这几乎总是一个坏主意,因为它会在每次渲染时重新创建,这意味着孩子将不断地重新安装。
【解决方案3】:

这是我以前见过的一个有用组件的示例(不确定该授权给谁),它可能更具声明性:

const ConditionalWrap = ({ condition, wrap, children }) => (
  condition ? wrap(children) : children
);

用例:

// This children of this MaybeInAModal component will appear as-is or within a modal
// depending on whether "shouldOpenInModal" is truthy
const MaybeInAModal = ({ children, shouldOpenInModal }) => {
  return (
    <ConditionalWrap
      condition={shouldOpenInModal}
      wrap={wrappedChildren => (<Modal>{wrappedChildren}</Modal>)}
        {children}
    </ConditionalWrap>
  );
}

【讨论】:

  • 我是从 kitze 那里看到的。但我不确定他是从别人那里得到这个主意的
  • 我也不是。这是弹出的第一个结果,我认为它是源 - 或者至少更接近它;)。
  • 你应该以声明的方式使用wrap,而不是作为一个函数来保持更多的“反应”精神
  • 你如何使它更具声明性@vsync?我认为渲染道具符合 React 的精神?
【解决方案4】:

还有另一种方式 你可以使用引用变量

let Wrapper = React.Fragment //fallback in case you dont want to wrap your components

if(someCondition) {
    Wrapper = ParentComponent
}

return (
    <Wrapper parentProps={parentProps}>
        <Child></Child>
    </Wrapper>

)

【讨论】:

  • 可以把前半部分浓缩成let Wrapper = someCondition ? ParentComponent : React.Fragment
  • 这很棒,但有时您希望保留代码声明性,这意味着它只返回 JSX
  • 我得到一个 error React.Fragment can only have 'key' and 'children' 因为我将一些道具传递给“”,例如 "className" 等等
  • @vsync 您需要为道具添加条件以及诸如 propId={someCondition? parentProps:未定义} ..
  • 我知道 :) 我写这篇文章是为了给遇到这个问题的其他人提供文档,所以谷歌会在搜索结果中缓存这些关键字的页面
【解决方案5】:
const ConditionalWrapper = ({ condition, wrapper, children }) => 
  condition ? wrapper(children) : children;

你想包装的组件

<ConditionalWrapper
   condition={link}
   wrapper={children => <a href={link}>{children}</a>}>
   <h2>{brand}</h2>
</ConditionalWrapper>

也许这篇文章可以帮助你更多 https://blog.hackages.io/conditionally-wrap-an-element-in-react-a8b9a47fab2

【讨论】:

    【解决方案6】:

    您应该按照here 的描述使用 JSX if-else。这样的事情应该可以工作。

    App = React.creatClass({
        render() {
            var myComponent;
            if(typeof(this.props.url) != 'undefined') {
                myComponent = <myLink url=this.props.url>;
            }
            else {
                myComponent = <myDiv>;
            }
            return (
                <div>
                    {myComponent}
                </div>
            )
        }
    });
    

    【讨论】:

      【解决方案7】:

      你也可以像这样使用 util 函数:

      const wrapIf = (conditions, content, wrapper) => conditions
              ? React.cloneElement(wrapper, {}, content)
              : content;
      

      【讨论】:

        【解决方案8】:

        使用 react 和 Typescript

        let Wrapper = ({ children }: { children: ReactNode }) => <>{children} </>
        
        if (this.props.link) {
            Wrapper = ({ children }: { children: ReactNode }) => <Link to={this.props.link}>{children} </Link>
        }
        
        return (
            <Wrapper>
                <i>
                    {this.props.count}
                </i>
            </Wrapper>
        )

        【讨论】:

          【解决方案9】:

          一个功能组件,它呈现 2 个组件,一个被包装,另一个没有。

          方法一:

          // The interesting part:
          const WrapIf = ({ condition, With, children, ...rest }) => 
            condition 
              ? <With {...rest}>{children}</With> 
              : children
          
           
              
          const Wrapper = ({children, ...rest}) => <h1 {...rest}>{children}</h1>
          
          
          // demo app: with & without a wrapper
          const App = () => [
            <WrapIf condition={true} With={Wrapper} style={{color:"red"}}>
              foo
            </WrapIf>
            ,
            <WrapIf condition={false} With={Wrapper}>
              bar
            </WrapIf>
          ]
          
          ReactDOM.render(<App/>, document.body)
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

          也可以这样使用:

          <WrapIf condition={true} With={"h1"}>
          

          方法二:

          // The interesting part:
          const Wrapper = ({ condition, children, ...props }) => condition 
            ? <h1 {...props}>{children}</h1>
            : <React.Fragment>{children}</React.Fragment>;   
              // stackoverflow prevents using <></>
            
          
          // demo app: with & without a wrapper
          const App = () => [
            <Wrapper condition={true} style={{color:"red"}}>
              foo
            </Wrapper>
            ,
            <Wrapper condition={false}>
              bar
            </Wrapper>
          ]
          
          ReactDOM.render(<App/>, document.body)
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

          【讨论】:

            【解决方案10】:

            使用提供的解决方案存在性能问题: https://medium.com/@cowi4030/optimizing-conditional-rendering-in-react-3fee6b197a20

            React 将在下一次渲染时卸载 &lt;Icon&gt; 组件。 Icon 在 JSX 中以不同的顺序存在两次,如果您在下一次渲染时更改 props.link,React 将卸载它。在这种情况下&lt;Icon&gt; 它不是一个繁重的组件并且可以接受,但是如果您正在寻找其他解决方案:

            https://codesandbox.io/s/82jo98o708?file=/src/index.js

            https://thoughtspile.github.io/2018/12/02/react-keep-mounted/

            【讨论】:

            • avoid link only answers。答案“仅仅是指向外部网站的链接”may be deleted
            • @Quentin 我已经发布了这个问题的答案,为什么它很重要,用户需要记住这一点。我认为这个描述就足够了,它不仅仅是链接的答案,但如果你想说更多的话..随时编辑它。
            • 图标在任何情况下都会被卸载,因为父级将被更改
            猜你喜欢
            • 1970-01-01
            • 2020-03-11
            • 1970-01-01
            • 2017-02-20
            • 2017-12-20
            • 2023-01-02
            • 2016-12-01
            • 1970-01-01
            • 2015-10-05
            相关资源
            最近更新 更多