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

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.


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

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>

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>

)

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;

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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.