如何有条件地包装React组件?


85

我有一个组件,有时有时需要呈现为<anchor>和,有时需要呈现为<div>。在prop我读来确定这一点,是this.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作为局部变量。
苏珊(Sulthan)2015年

伙计,我喜欢它。越来越多地了解ES6,它总是会提高可读性。感谢您的额外提示!
布兰登·达勒姆

1
什么是“最终锁定”?
克里斯·哈里森

Answers:


92

只需使用一个变量。

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和其他代码一样。如果要减少重复,请使用函数和变量。


21

创建一个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>
);

4
HOC应该慢慢死:P
Jamie Hutber '19

这个词HOC很丑。它只是放在中间的一个函数。我真的取代了这个突然流行的名字“ HPC”。放置在...旧概念之间数十年的简单功能的高级性是什么。
vsync

12

这是我看过用来完成工作的有用组件的一个示例(不确定谁要认可它):

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

用例:

<ConditionalWrap condition={someCondition}
  wrap={children => (<a>{children}</a>)} // Can be anything
>
  This text is passed as the children arg to the wrap prop
</ConditionalWrap>


我从风俗中看到了。但我不知道他从别人的想法
安东尼

我也不是。这是第一个弹出的结果,我认为它是来源-或至少离它更近;)。
罗伊·普林斯

您应该使用的wrap声明,而不是作为一个功能让事情变得更“响应” -spirit
VSYNC

您将如何使其更具声明性的@vsync?我认为渲染道具符合React的精神吗?
antony

10

还有另一种方法可以使用参考变量

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
mpoisot,

这很棒,但是有时您希望保持代码为声明性,这意味着它仅返回JSX
vsync

我收到错误消息 React.Fragment can only have 'key' and 'children'是因为我将一些道具传递给“ <Wrapper>”,例如“ className”,所以
vsync

@vsync,您需要为道具以及诸如propId = {someCondition?parentProps:undefined} ..
Avinash

1
我知道:)我写这篇文章是为了给其他遇到此问题的人提供文档,因此Google会在这些关键字的搜索结果中缓存此页面
vsync

1

您还可以使用如下所示的util函数:

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

0

如果其他的描述,您应该使用JSX这里。这样的事情应该起作用。

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

-2

一个可呈现2个组件的功能组件,一个组件被包装,而另一个组件则没有。

方法1:

// 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"}>

方法2:

// 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>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.