Reagire componente stateless funzionale, PureComponent, Component; quali sono le differenze e quando dovremmo usare cosa?


189

Sono venuto a sapere che da React v15.3.0 , abbiamo una nuova classe base chiamata PureComponent da estendere con PureRenderMixin integrato. Quello che capisco è che, sotto il cofano, questo utilizza un confronto superficiale di oggetti di scena all'interno shouldComponentUpdate.

Ora abbiamo 3 modi per definire un componente React:

  1. Componente stateless funzionale che non estende alcuna classe
  2. Un componente che estende la PureComponentclasse
  3. Un componente normale che estende la Componentclasse

Qualche tempo fa chiamavamo componenti senza stato come componenti puri o anche componenti stupidi. Sembra che l'intera definizione della parola "puro" sia ora cambiata in React.

Sebbene comprenda le differenze di base tra questi tre, non sono ancora sicuro di scegliere cosa . Inoltre, quali sono gli impatti sulle prestazioni e i compromessi di ciascuno?


Aggiornamento :

Queste sono le domande che mi aspetto di essere chiarito:

  • Dovrei scegliere di definire i miei componenti semplici come funzionali (per motivi di semplicità) o estendere la PureComponentclasse (per motivi di prestazioni)?
  • L'aumento delle prestazioni che ottengo un vero compromesso per la semplicità che ho perso?
  • Avrei mai bisogno di estendere la Componentclasse normale quando posso sempre usare PureComponentper prestazioni migliori?

Risposte:


315

Come decidi tu, come scegliere tra questi tre in base allo scopo / dimensione / oggetti di scena / comportamento dei nostri componenti?

L'estensione da React.PureComponento verso React.Componentcon un shouldComponentUpdatemetodo personalizzato ha implicazioni sulle prestazioni. L'uso di componenti funzionali senza stato è una scelta "architettonica" e non ha alcun vantaggio prestazionale pronto all'uso (ancora).

  • Per componenti semplici, solo di presentazione che devono essere facilmente riutilizzati, preferisci i componenti funzionali senza stato. In questo modo sei sicuro che siano disaccoppiati dall'attuale logica dell'app, che sono estremamente facili da testare e che non hanno effetti collaterali imprevisti. L'eccezione è se per qualche motivo ne hai molti o se hai davvero bisogno di ottimizzare il loro metodo di rendering (come non puoi definire shouldComponentUpdateper un componente funzionale senza stato).

  • Estendi PureComponentse sai che il tuo output dipende da semplici oggetti di scena / stato ("semplice" significa che nessuna struttura di dati nidificata, poiché PureComponent esegue un confronto superficiale) E hai bisogno / puoi ottenere alcuni miglioramenti delle prestazioni.

  • Estendi Componente implementa il tuo shouldComponentUpdatese hai bisogno di alcuni miglioramenti delle prestazioni eseguendo una logica di confronto personalizzata tra oggetti di scena successivi / attuali e stato. Ad esempio, puoi eseguire rapidamente un confronto approfondito utilizzando lodash # isEqual:

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }
    

Inoltre, implementare le proprie shouldComponentUpdateo estenderle PureComponentsono ottimizzazioni, e come al solito dovresti iniziare a esaminarle solo se hai problemi di prestazioni ( evita le ottimizzazioni premature ). Come regola generale, provo sempre a fare queste ottimizzazioni dopo che l'applicazione è funzionante, con la maggior parte delle funzionalità già implementate. È molto più facile concentrarsi sui problemi di prestazioni quando effettivamente si frappongono.

Più dettagli

Componenti funzionali senza stato:

Questi sono definiti semplicemente usando una funzione. Poiché non esiste uno stato interno per un componente senza stato, l'output (ciò che viene reso) dipende solo dai puntelli forniti come input per questa funzione.

Professionisti:

  • Il modo più semplice possibile per definire un componente in React. Se non è necessario gestire alcuno stato, perché preoccuparsi delle classi e dell'eredità? Una delle principali differenze tra una funzione e una classe è che con la funzione si è certi che l'output dipende solo dall'input (non dalla cronologia delle precedenti esecuzioni).

  • Idealmente nella tua app dovresti mirare ad avere il maggior numero possibile di componenti senza stato, perché ciò normalmente significa che hai spostato la tua logica al di fuori del livello di visualizzazione e spostata in qualcosa come Redux, il che significa che puoi testare la tua vera logica senza dover eseguire il rendering di nulla (molto più facile da testare, più riutilizzabile, ecc.).

Contro:

  • Nessun metodo del ciclo di vita. Non hai modo di definire componentDidMounte altri amici. Normalmente lo fai all'interno di un componente superiore nella gerarchia in modo da poter trasformare tutti i bambini in apolidi.

  • Non è possibile controllare manualmente quando è necessario un nuovo rendering, poiché non è possibile definire shouldComponentUpdate. Un nuovo rendering avviene ogni volta che il componente riceve nuovi oggetti di scena (nessun modo per confrontare superficialmente, ecc.). In futuro, React potrebbe ottimizzare automaticamente i componenti senza stato, per ora ci sono alcune librerie che puoi usare. Poiché i componenti senza stato sono solo funzioni, in pratica è il classico problema della "memorizzazione delle funzioni".

  • I riferimenti non sono supportati: https://github.com/facebook/react/issues/4936

Un componente che estende la classe PureComponent VS Un componente normale che estende la classe Component:

React era solito avere un PureRenderMixinche potevi associare a una classe definita usando la React.createClasssintassi. Il mixin semplicemente definirebbe un shouldComponentUpdateconfronto superficiale tra i props successivi e lo stato successivo per verificare se qualcosa è cambiato. Se non cambia nulla, non è necessario eseguire un nuovo rendering.

Se si desidera utilizzare la sintassi ES6, non è possibile utilizzare i mixin. Quindi per comodità React ha introdotto una PureComponentclasse che puoi ereditare invece di usare Component. PureComponentimplementa semplicemente shouldComponentUpdateallo stesso modo di PureRendererMixin. È principalmente una cosa comoda, quindi non è necessario implementarlo da soli, poiché un confronto superficiale tra lo stato attuale / successivo e gli oggetti di scena è probabilmente lo scenario più comune che può darti alcune prestazioni veloci.

Esempio:

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

Come puoi vedere, l'output dipende da props.imageUrle props.username. Se in un componente genitore esegui il rendering <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />con gli stessi puntelli, React chiamerebbe renderogni volta, anche se l'output sarà esattamente lo stesso. Ricorda però che React implementa dom diffing, quindi il DOM non verrebbe effettivamente aggiornato. Tuttavia, eseguire il diff di dom può essere costoso, quindi in questo scenario sarebbe uno spreco.

Se invece il UserAvatarcomponente si estende PureComponent, viene eseguito un confronto superficiale. E poiché oggetti di scena e nextProps sono uguali, rendernon verranno chiamati affatto.

Note sulla definizione di "puro" in React:

In generale, una "funzione pura" è una funzione che valuta sempre lo stesso risultato dato lo stesso input. L'output (per React, questo è ciò che viene restituito dal rendermetodo) non dipende da alcuna storia / stato e non ha effetti collaterali (operazioni che cambiano il "mondo" al di fuori della funzione).

In React, i componenti senza stato non sono necessariamente componenti puri secondo la definizione precedente se si chiama "senza stato" un componente che non chiama this.setStatee che non utilizza this.state.

In effetti, in a PureComponent, è ancora possibile eseguire effetti collaterali durante i metodi del ciclo di vita. Ad esempio, è possibile inviare una richiesta Ajax all'interno componentDidMountoppure eseguire alcuni calcoli DOM per regolare dinamicamente l'altezza di un div all'interno render.

La definizione di "Componenti stupidi" ha un significato più "pratico" (almeno nella mia comprensione): un componente stupido "viene detto" cosa fare da un componente genitore tramite oggetti di scena e non sa come fare le cose ma usa oggetti di scena callbacks invece.

Esempio di "intelligente" AvatarComponent:

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.expandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

Esempio di "stupido" AvatarComponent:

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

Alla fine direi che "stupido", "apolide" e "puro" sono concetti abbastanza diversi che a volte possono sovrapporsi, ma non necessariamente, a seconda principalmente del caso d'uso.


1
Apprezzo molto la tua risposta e la conoscenza che hai condiviso. Ma la mia vera domanda è quando dovremmo scegliere cosa? . Per lo stesso esempio che hai citato nella tua risposta, come devo definirlo? Dovrebbe essere un componente stateless funzionale (se sì, perché?) O estendere PureComponent (perché?) O estendere la classe Component (di nuovo perché?). Come decidi tu, come scegliere tra questi tre in base allo scopo / dimensione / oggetti di scena / comportamento dei nostri componenti?
Yadhu Kiran,

1
Nessun problema. Per il componente stateless funzionale, c'è un elenco di pro / contro che posso prendere in considerazione per decidere se sarebbe una buona scelta. Ti risponde prima? Proverò a rispondere un po 'di più alla domanda scelta.
fabio.sussetto,

2
I componenti funzionali vengono sempre renderizzati nuovamente quando il componente padre viene aggiornato, anche se non lo usano propsaffatto. esempio .
AlexM,

1
Questa è una delle risposte più complete che ho letto da un po 'di tempo. Ottimo lavoro. Un commento sulla prima frase: quando si estende PureComponent, non è necessario implementare shouldComponentUpdate(). Dovresti vedere un avviso se lo fai effettivamente.
jjramos,

1
Per ottenere prestazioni reali, dovresti provare a utilizzare i PureComponentcomponenti che hanno proprietà oggetto / array nidificate. Ovviamente devi essere consapevole di ciò che sta accadendo. Se ho capito bene, se non stai mutando direttamente oggetti di scena / stato (cosa che React cerca di impedirti di fare con gli avvertimenti) o tramite una libreria esterna, allora dovresti andare bene PureComponentinvece di usare Componentpraticamente ovunque ... con l'eccezione di componenti molto semplici in cui può effettivamente essere più veloce NON usarlo - vedi news.ycombinator.com/item?id=14418576
Matt Browne

28

non sono un genio nel reagire, ma dalla mia comprensione possiamo usare ogni componente nelle seguenti situazioni

  1. Componente senza stato: si tratta del componente che non ha un ciclo di vita, pertanto è necessario utilizzare tali componenti nel rendering di elementi ripetuti del componente padre, ad esempio il rendering dell'elenco di testo che visualizza solo le informazioni e non ha alcuna azione da eseguire.

  2. Componente puro: questi sono gli articoli che hanno un ciclo di vita e restituiranno sempre lo stesso risultato quando viene fornito un set specifico di oggetti di scena. Tali componenti possono essere utilizzati durante la visualizzazione di un elenco di risultati o dati di oggetti specifici che non hanno elementi figlio complessi e utilizzati per eseguire operazioni che incidono solo su se stesso. tale elenco di schede utente o elenco di schede prodotti (informazioni di base sul prodotto) e solo l'azione che l'utente può eseguire è fare clic per visualizzare la pagina dei dettagli o aggiungere al carrello.

  3. Componenti normali o componenti complessi: ho usato il termine componente complesso perché di solito sono i componenti a livello di pagina e sono costituiti da molti componenti figlio e poiché ciascuno di essi può comportarsi in modo univoco, quindi non si può essere sicuri al 100% che lo farà rende lo stesso risultato su un determinato stato. Come dicevo di solito, questi dovrebbero essere usati come componenti del contenitore


1
Questo approccio potrebbe funzionare, ma potresti perdere grandi guadagni in termini di prestazioni. L'uso PureComponentdi componenti a livello di root e componenti nella parte superiore della gerarchia è in genere il punto in cui si otterrebbero i maggiori miglioramenti delle prestazioni. Naturalmente è necessario evitare oggetti di scena mutanti e dichiarare direttamente che i componenti puri funzionino correttamente, ma gli oggetti mutanti direttamente sono un anti-pattern in React comunque.
Matt Browne,

5
  • React.Componentè il componente "normale" predefinito. Li dichiarate usando la classparola chiave e extends React.Component. Pensa a loro come a una classe, con metodi di cicli di vita, gestori di eventi e qualsiasi metodo.

  • React.PureComponentè uno React.Componentche implementa shouldComponentUpdate()con una funzione che fa un confronto superficiale tra suo propse state. Devi usarlo forceUpdate()se sai che il componente ha oggetti di scena o indica dati nidificati che sono cambiati e vuoi renderizzare nuovamente. Quindi non sono grandi se hai bisogno di componenti per il rendering di nuovo quando matrici o oggetti che passi come oggetti di scena o impostati nel tuo stato cambiano.

  • I componenti funzionali sono quelli che non hanno funzioni del ciclo di vita. Sono presumibilmente apolidi, ma sono così carini e puliti che ora abbiamo ganci (dal React 16.8) in modo da poter ancora avere uno stato. Quindi immagino siano solo "componenti puliti".

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.