Come avvolgo condizionatamente un componente React?


90

Ho un componente che a volte dovrà essere renderizzato come un <anchor>e altre volte come un file <div>. L' propho letto per determinarlo, è this.props.url.

Se esiste, devo rendere il componente avvolto in un file <a href={this.props.url}>. Altrimenti viene semplicemente renderizzato come file <div/>.

Possibile?

Questo è quello che sto facendo in questo momento, ma sento che potrebbe essere semplificato:

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

AGGIORNARE:

Ecco il blocco finale. Grazie per il suggerimento, @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;
    }
}

Puoi anche spostarti const baseClasses =in quel if (this.props.link)ramo. Dato che stai usando ES6, puoi anche semplificare un po ' const {link, className} = this.props;e poi usare linke classNamecome variabili locali.
Sulthan

Amico, lo adoro. Imparare sempre di più su ES6 e migliora sempre e solo la leggibilità. Grazie per il suggerimento extra!
Brandon Durham

1
Cos'è un "blocco finale"?
Chris Harrison

Risposte:


94

Usa solo una variabile.

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;

oppure è possibile utilizzare una funzione di supporto per eseguire il rendering dei contenuti. JSX è un codice come un altro. Se vuoi ridurre le duplicazioni, usa funzioni e variabili.


23

Crea un HOC (componente di ordine superiore) per avvolgere il tuo elemento:

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 dovrebbe morire lentamente: P
Jamie Hutber

Il termine HOCè orribile. È semplicemente una funzione posta nel mezzo. Ho davvero sostituito questo nome improvvisamente trendy "HPC". cosa c'è di così alto in una semplice funzione che si trova tra ... vecchio concetto per decenni.
vsync

13

Ecco un esempio di un componente utile che ho visto utilizzare (non so a chi accreditarlo) che fa il lavoro:

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

Caso d'uso:

<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
Il credito dovrebbe probabilmente andare qui: gist.github.com/kitze/23d82bb9eb0baabfd03a6a720b1d637f
Roy Prins

L'ho visto da Kitze. Ma non ero sicuro che avesse avuto l'idea da qualcun altro
antony

Nemmeno io. Questo è stato il primo risultato che è apparso e ho pensato che fosse la fonte - o almeno più vicino ad esso;).
Roy Prins

Dovresti usare il wrapdichiarativo e non come funzione per mantenere le cose più "React" -spirit
vsync

Come renderesti più dichiarativo @vsync? Pensavo che gli oggetti di rendering fossero nello spirito di React?
antony

10

C'è un altro modo per utilizzare una variabile di riferimento

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>

)

Puoi condensare il primo tempo alet Wrapper = someCondition ? ParentComponent : React.Fragment
mpoisot

È fantastico, ma a volte si desidera mantenere il codice dichiarativo , il che significa che restituisce solo JSX
vsync

Ricevo un errore React.Fragment can only have 'key' and 'children' perché passo alcuni oggetti di scena a "<Wrapper>" come "className" e così
vsync

@vsync devi aggiungere anche una condizione per gli oggetti di scena qualcosa come propId = {someCondition? parentProps: undefined} ..
Avinash

1
Lo so :) Lo stavo scrivendo per motivi di documentazione per altri che vengono qui con questo problema, quindi Google memorizzerà questa pagina nei risultati di ricerca per quelle parole chiave
vsync

1

Puoi anche usare una funzione util come questa:

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


0

Dovresti usare un JSX if-else come descritto qui . Qualcosa di simile dovrebbe funzionare.

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

Un componente funzionale che rende 2 componenti, uno è avvolto e l'altro no.

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

Questo può anche essere usato in questo modo:

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

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.