Come posso resettare un componente React incluso tutto lo stato transitivamente raggiungibile?


93

Occasionalmente ho componenti di reazione che sono concettualmente stateful che voglio ripristinare. Il comportamento ideale sarebbe equivalente a rimuovere il vecchio componente e leggere un nuovo componente incontaminato.

React fornisce un metodo setStateche consente di impostare il proprio stato esplicito dei componenti, ma che esclude lo stato implicito come il focus del browser e lo stato del form, ed esclude anche lo stato dei suoi figli. Cogliere tutto quello stato indiretto può essere un compito difficile, e preferirei risolverlo in modo rigoroso e completo piuttosto che giocare a whack-a-mole con ogni nuovo bit di stato sorprendente.

C'è un'API o un pattern per farlo?

Modifica: ho fatto un esempio banale dimostrando l' this.replaceState(this.getInitialState())approccio e confrontandolo con l' this.setState(this.getInitialState())approccio: jsfiddle - replaceStateè più robusto.

Risposte:


206

Per garantire che lo stato del browser implicito menzionato e lo stato dei figli vengano ripristinati, è possibile aggiungere un keyattributo al componente a livello di root restituito da render; quando cambia, quel componente verrà gettato via e creato da zero.

render: function() {
    // ...
    return <div key={uniqueId}>
        {children}
    </div>;
}

Non esiste una scorciatoia per ripristinare lo stato locale del singolo componente.


1
Solo per curiosità quando dici "il componente verrà gettato via e creato da zero", intendi che il DOM virtuale verrà gettato via e creato da zero ma gli aggiornamenti DOM verranno comunque effettuati in base all'algoritmo del diff?
nimgrg

1
@nimgrg No, verranno ricreati anche i veri elementi DOM. (Il DOM virtuale è già ricreato su ogni rendering, quindi se vuoi mantenere il DOM reale, semplicemente non impostare un keyin questo caso.)
Sophie Alpert

3
@EamonNerbonne Su React 0.8, le chiavi vengono utilizzate solo per distinguere tra elementi di pari livello in un array e sono state ignorate alla radice. Vedi github.com/facebook/react/issues/590 .
Sophie Alpert

Nota replaceState()e getInitialState()sono entrambi deprecati nel nuovo reactjs in stile classe ES6. Usa this.setState(this._getInitialState())invece. Inoltre, non puoi nominare la tua funzione di inizializzazione dello stato getInitialState()- react lancia un avvertimento - chiamala in qualsiasi altra maniera e fallo esplicitamente state = this._getInitialState()nel livello più alto della classe.
thom_nic

2
C'è un modo per farlo dall'interno di un componente senza richiedere che l'esterno passi un puntello chiave?
trusktr

14

In realtà dovresti evitare replaceStatee usare setStateinvece.

I documenti dicono che replaceState"potrebbe essere rimosso completamente in una futura versione di React." Penso che sarà sicuramente rimosso perché replaceStatenon si concilia con la filosofia di React. Facilita il fatto che un componente React inizi a sembrare un coltello svizzero. Ciò contrasta la crescita naturale di un componente React che diventa più piccolo e più mirato.

In React, se devi sbagliare sulla generalizzazione o sulla specializzazione: punta alla specializzazione. Come corollario, l'albero degli stati per il tuo componente dovrebbe avere una certa parsimonia (va bene infrangere con gusto questa regola se stai impalcando un prodotto nuovo di zecca).

Comunque è così che lo fai. Simile alla risposta (accettata) di Ben sopra, ma in questo modo:

this.setState(this.getInitialState());

Inoltre (come ha detto anche Ben) per ripristinare lo "stato del browser" è necessario rimuovere quel nodo DOM. Sfrutta la potenza del vdom e usa un nuovo keypuntello per quel componente. Il nuovo rendering sostituirà quel componente all'ingrosso.

Riferimento: https://facebook.github.io/react/docs/component-api.html#replacestate


2
Questo non funzionerà se lo stato corrente include proprietà che non si trovano nello stato iniziale. Anche se non credo che sia un grande "stato" di cose in cui trovarsi, a meno che tu non possa in qualche modo impedirlo, vorrei evitare rotture sorprendenti. Sarei OK con un ripristino che si arresta in modo anomalo in alcuni casi, ma non un ripristino che danneggia leggermente lo stato.
Eamon Nerbonne

Non sono sicuro di seguirlo. Stai dicendo che questo non è equivalente a this.replaceState? Perché lo è.
wle8300

1
@ williamle8300 Non sarebbero equivalenti se una nuova proprietà da dichiarare fosse aggiunta da qualche parte nel ciclo di vita del componente tra le chiamate a getInitialState()(ad esempio, in un gestore onClick). In questo caso, quella nuova proprietà sarebbe ancora presente dopo il 2 ° getInitialState().
Graham

@ Graham Credo che sia una cattiva pratica introdurre un nuovo stato in un componente che non è già dichiarato getInitialState. Proprio come dichiarare tutte le tue variabili in una funzione JS nella parte superiore della funzione. Nota a margine: la soluzione di Ben Alpert è quasi identica alla mia ... ed è un manutentore ufficiale di React!
wle8300

1
Temo che la funzione getInitialState (), e quindi la risposta, sia deprecata. Sarebbe bello un aggiornamento.
Martin R.

9

L'aggiunta di un keyattributo all'elemento che è necessario reinizializzare, lo ricaricherà ogni volta che propso si stateassocia all'elemento cambia.

chiave = {nuova data (). getTime ()}

Ecco un esempio:

render() {
  const items = (this.props.resources) || [];
  const totalNumberOfItems = (this.props.resources.noOfItems) || 0;

  return (
    <div className="items-container">
      <PaginationContainer
        key={new Date().getTime()}
        totalNumberOfItems={totalNumberOfItems}
        items={items}
        onPageChange={this.onPageChange}
      />
    </div>
  );
}

1
Oppure puoi aggiungere una semplice variabile contatore intero come chiave
minimalista
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.