Як умовно обернути компонент React?


86

У мене є компонент, який іноді потрібно буде відображати як a, <anchor>а інший раз як a <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;а потім використовувати linkі classNameяк локальні змінні.
Султан

Чоловіче, мені це подобається. Дізнаючись більше про ES6, він завжди покращує читабельність. Дякуємо за додаткову пораду!
Брендон Дарем

1
Що таке "остаточне блокування"?
Кріс Гаррісон

Відповіді:


93

Просто використовуйте змінну.

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 - це код, як і будь-який інший. Якщо ви хочете зменшити кількість копій, використовуйте функції та змінні.


22

Створіть 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

Цей термін 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>

2
Кредит, ймовірно, повинен йти сюди: gist.github.com/kitze/23d82bb9eb0baabfd03a6a720b1d637f
Рой Прінс,

Я це бачив від Кітце. Але я не був впевнений, що ідею він отримав від когось іншого
Ентоні

Я теж. Це був перший результат, який з’явився, і я припустив, що це джерело - або принаймні ближче до нього;).
Рой Прінс

Вам слід використовувати wrapдекларативно, а не як функцію, щоб тримати речі більш "React"
-spirit

Як би ви зробили його більш декларативним @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} ..
Авінаш

1
Я знаю :) Я писав це заради документації для інших, хто прийшов сюди з цією проблемою, тому Google буде кешувати цю сторінку в результатах пошуку за цими ключовими словами
vsync

1

Ви також можете використовувати функцію утиліту, як це:

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>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.