Wie verpacke ich eine React-Komponente bedingt?

85

Ich habe eine Komponente, die manchmal als <anchor>und manchmal als gerendert werden muss <div>. Das habe propich gelesen, um das festzustellen, ist this.props.url.

Wenn es existiert, muss ich die Komponente in ein gerendertes rendern <a href={this.props.url}>. Andernfalls wird es nur als gerendert <div/>.

Möglich?

Dies ist, was ich gerade mache, aber ich denke, es könnte vereinfacht werden:

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

AKTUALISIEREN:

Hier ist die endgültige Überbrückung. Danke für den Tipp, @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;
    }
}
Brandon Durham
quelle
Sie können auch const baseClasses =in diesen if (this.props.link)Zweig wechseln . Da Sie ES6 verwenden, können Sie auch ein wenig vereinfachen, const {link, className} = this.props;indem Sie linkund classNameals lokale Variablen verwenden.
Sulthan
Mann, ich liebe es. Wenn Sie mehr und mehr über ES6 lernen, wird die Lesbarkeit immer verbessert. Danke für den zusätzlichen Tipp!
Brandon Durham
1
Was ist eine "endgültige Überbrückung"?
Chris Harrison

Antworten:

92

Verwenden Sie einfach eine Variable.

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;

Sie können auch eine Hilfsfunktion verwenden, um den Inhalt zu rendern. JSX ist Code wie jeder andere. Wenn Sie Duplikate reduzieren möchten, verwenden Sie Funktionen und Variablen.

Sulthan
quelle
21

Erstellen Sie ein HOC (Komponente höherer Ordnung) zum Umschließen Ihres Elements:

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>
);
Asynchronisieren
quelle
4
HOC sollte langsam sterben: P
Jamie Hutber
Der Begriff HOCist abscheulich. Es ist lediglich eine Funktion, die in der Mitte platziert ist. Ich verdränge diesen plötzlich trendigen Namen "HPC" wirklich. Was ist so hochwertig an einer einfachen Funktion, die zwischen ... altem Konzept seit Jahrzehnten liegt.
vsync
12

Hier ist ein Beispiel für eine hilfreiche Komponente, die ich verwendet habe (nicht sicher, bei wem ich sie akkreditieren soll), die die Aufgabe erfüllt:

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

Anwendungsfall:

<ConditionalWrap condition={someCondition}
  wrap={children => (<a>{children}</a>)} // Can be anything
>
  This text is passed as the children arg to the wrap prop
</ConditionalWrap>
Antony
quelle
2
Kredit sollte wahrscheinlich hier gehen: gist.github.com/kitze/23d82bb9eb0baabfd03a6a720b1d637f
Roy Prins
Das habe ich von kitze aus gesehen. Aber ich war mir nicht sicher, ob er die Idee von jemand anderem bekommen hatte
antony
Ich auch nicht. Dies war das erste Ergebnis, das auftauchte und ich nahm an, dass es die Quelle ist - oder zumindest näher daran;).
Roy Prins
Sie sollten das wrapdeklarativ und nicht als Funktion verwenden, um die Dinge "React" -spirit
vsync
Wie würden Sie es @vsync deklarativer machen? Ich dachte, Render Requisiten wären im Geiste von React?
Antony
10

Es gibt eine andere Möglichkeit, eine Referenzvariable zu verwenden

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>

)
Avinash
quelle
Sie können die erste Hälfte auflet Wrapper = someCondition ? ParentComponent : React.Fragment
mpoisot
Das ist großartig, aber manchmal möchten Sie den Code deklarativ halten , was bedeutet, dass er nur JSX
zurückgibt
Ich erhalte eine Fehlermeldung, React.Fragment can only have 'key' and 'children' weil ich einige Requisiten wie "className" an "<Wrapper> " übergebe und so
vsync
@vsync Sie müssen eine Bedingung für Requisiten hinzufügen, so etwas wie propId = {someCondition? parentProps: undefined} ..
Avinash
1
Ich weiß :) Ich habe dies aus Gründen der Dokumentation für andere Personen geschrieben, die mit diesem Problem hierher kommen.
Daher
1

Sie können auch eine Util-Funktion wie die folgende verwenden:

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

Sie sollten ein JSX if-else verwenden, wie hier beschrieben . So etwas sollte funktionieren.

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>
        )
    }
});
user2027202827
quelle
-2

Eine Funktionskomponente, die zwei Komponenten rendert, eine ist verpackt und die andere nicht.

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

Dies kann auch folgendermaßen verwendet werden:

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

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

vsync
quelle