Utilizzare state o refs nei componenti del modulo React.js?


116

Sto iniziando con React.js e voglio creare un modulo semplice ma nella documentazione ho trovato due modi per farlo.

Il primo sta usando Refs :

var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="Your name" ref="author" />
        <input type="text" placeholder="Say something..." ref="text" />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

E il secondo sta usando lo stato all'interno del componente React:

var TodoTextInput = React.createClass({
  getInitialState: function() {
    return {
      value: this.props.value || ''
    };
  },

  render: function() /*object*/ {
    return (
      <input className={this.props.className}
      id={this.props.id}
      placeholder={this.props.placeholder}
      onBlur={this._save}
      value={this.state.value}
      />
    );
  },

  _save: function() {
    this.props.onSave(this.state.value);
    this.setState({value: ''
  });
});

Non riesco a vedere i pro ei contro delle due alternative, se ne esistono. Grazie.


Mi manca qualcosa qui? Perché non usi l'oggetto evento per ottenere i valori del modulo? Questa sembra essere l'unica ragione per utilizzare un modulo qui in primo luogo. Se non si utilizza il comportamento di invio predefinito e si hanno ref sugli input non è necessario racchiuderli in un modulo.
NectarSoft

Risposte:


143

La versione breve: evitare ref.


Sono dannosi per la manutenibilità e perdono molta della semplicità fornita dal rendering del modello WYSIWYG.

Hai un modulo. È necessario aggiungere un pulsante che ripristina il modulo.

  • arbitri:
    • manipolare il DOM
    • render descrive come appariva il form 3 minuti fa
  • stato
    • setState
    • render descrive come appare il form

Hai un campo numero CCV in un input e alcuni altri campi nella tua applicazione che sono numeri. Ora è necessario imporre che l'utente inserisca solo numeri.

  • arbitri:
    • aggiungi un gestore onChange (non stiamo usando refs per evitarlo?)
    • manipola dom in onChange se non è un numero
  • stato
    • hai già un gestore onChange
    • aggiungi un'istruzione if, se non è valida non fare nulla
    • render viene chiamato solo se produrrà un risultato diverso

Eh, non importa, il Primo Ministro vuole che facciamo solo un riquadro rosso-ombra se non è valido.

  • arbitri:
    • make onChange handler chiama solo forceUpdate o qualcosa del genere?
    • rendere l'output di rendering basato su ... eh?
    • dove otteniamo il valore da convalidare nel rendering?
    • manipolare manualmente la proprietà className dom di un elemento?
    • mi sono perso
    • riscrivere senza ref?
    • leggi dal dom in render se siamo montati altrimenti presumo valido?
  • stato:
    • rimuovere l'istruzione if
    • rendere convalidato il rendering in base a this.state

Dobbiamo restituire il controllo al genitore. I dati sono ora in oggetti di scena e dobbiamo reagire ai cambiamenti.

  • arbitri:
    • implementare componentDidMount, componentWillUpdate e componentDidUpdate
    • diff manualmente gli oggetti di scena precedenti
    • manipolare la dom con il set minimo di modifiche
    • Hey! stiamo implementando reagire in reagire ...
    • c'è di più, ma mi fanno male le dita
  • stato:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

La gente pensa che gli arbitri siano "più facili" che mantenerli nello stato. Questo può essere vero per i primi 20 minuti, non è vero nella mia esperienza dopo. Mettiti in condizione di dire "Sì, lo farò in 5 minuti" invece di "Certo, riscriverò solo alcuni componenti".


3
Potresti spiegare un po 'di più su sed -e' s / this.state / this.props / '' s / handleChange / onChange / '-i form.js?
gabrielgiussi

1
No, intendo modifiche effettive al dom. React.findDOMNode(this.refs.foo). Se ad esempio cambi, this.refs.foo.props.barnon succederà nulla.
Brigante

1
ad esempio, se è necessario <input onChange={this.handleChange} value={this.state.foo} />cambiarlo in <input onChange={this.props.handleChange} value={this.props.foo} />o modificare le funzioni handleChange per richiamare i callback in props. Ad ogni modo, si tratta di alcuni piccoli cambiamenti evidenti.
Brigante

4
Non sono sicuro di essere l'unico a trovare la tua risposta un po 'confusa. Potresti mostrare alcuni esempi di codice per chiarire i tuoi punti?
Rishabh

2
Avere più di 50 input su uno schermo e renderli ciascuno su qualsiasi cambiamento di stato è indesiderabile. Componentizzare ogni inputcampo in cui ognuno mantiene il proprio stato è l'ideale. Ad un certo punto dobbiamo riconciliare questi vari stati indipendenti con un modello più ampio. Forse abbiamo un salvataggio automatico su un timer, o semplicemente salviamo su componentWillUnmountQuesto è dove trovo l' refsideale, durante la riconciliazione traiamo il statevalore da ciascuno ref, e nessuno è il più saggio. Sono d'accordo che nella maggior parte dei casi stateè la risposta, ma con un gran numero di inputs, l'utilizzo di un refsmodello corretto è un vantaggio per le prestazioni
lux

105

Ho visto alcune persone citare la risposta di cui sopra come un motivo per "non usare mai ref" e voglio dare la mia opinione (così come alcuni altri sviluppatori React con cui ho parlato).

Il sentimento "non usare ref" è corretto quando si parla di usarli per le istanze dei componenti. Significa che non dovresti usare refs come un modo per catturare istanze di componenti e chiamare metodi su di esse. Questo è il modo sbagliato di usare gli arbitri ed è quando gli arbitri vanno rapidamente a sud.

Il modo corretto (e molto utile) di usare i ref è quando li usi per ottenere un valore dal DOM. Ad esempio, se si dispone di un campo di input che allega un riferimento a tale input, è sufficiente acquisire il valore in un secondo momento tramite il riferimento. Senza in questo modo, è necessario eseguire un processo abbastanza orchestrato per mantenere aggiornato il campo di input con il proprio stato locale o con l'archivio di flusso, il che sembra non necessario.

Modifica 2019: ciao amici del futuro. Oltre a ciò che ho menzionato qualche anno fa ^, con React Hooks, i ref sono anche un ottimo modo per tenere traccia dei dati tra i rendering e non si limitano a catturare solo nodi DOM.


3
Il tuo ultimo paragrafo ha perfettamente senso, ma puoi chiarire il tuo secondo paragrafo? Qual è un esempio concreto di acquisizione di un'istanza di componente e di chiamata a un metodo che sarebbe considerato errato?
Danny Libin

2
Sono d'accordo con questo. Uso refs a meno che / fino a quando non sia necessario eseguire la convalida o la manipolazione del valore di un campo. Se devo convalidare in caso di modifica o modificare i valori a livello di codice, utilizzo lo stato.
Christopher Davies,

1
Anch'io sono d'accordo con questo. Durante una fase di scoperta mi sono avvicinato di proposito a uno schermo con un gran numero di input con ingenuità. Tutti i valori di input memorizzati in una mappa (nello stato) con chiave id. Inutile dire che le prestazioni hanno sofferto dall'impostazione dello stato e dal rendering di oltre 50 input (alcuni materiali-ui, che erano pesanti!) Su tali modifiche minori dell'interfaccia utente poiché un clic della casella di controllo non era l'ideale. Componentizzare ogni input in grado di mantenere il proprio stato sembrava l'approccio corretto. Se è necessaria la riconciliazione, basta esaminare refse ottenere il valore dello stato. Sembra davvero uno schema carino.
lux

2
Sono completamente d'accordo. La risposta accettata è troppo vaga secondo me.
James Wright

Sono d'accordo. Durante la progettazione di un componente Form generale, questo porta alla luce i punti deboli dei componenti controllati e della gestione della messa a fuoco, della gestione degli errori, ecc. Non è possibile infatti avere un'architettura pulita. Parla con me se necessario. Sto spostando i miei componenti su rif.
kushalvm

6

TL; DR In generale, refsvai contro la filosofia dichiarativa di React , quindi dovresti usarli come ultima risorsa. Usa state / propsquando possibile.


Per capire dove usi refsvs state / props, diamo un'occhiata ad alcuni dei principi di progettazione seguiti da React.

Documentazione di Per React surefs

Evita di usare ref per tutto ciò che può essere fatto in modo dichiarativo.

Principi di progettazione di Per React sui portelli di fuga

Se un pattern utile per la creazione di app è difficile da esprimere in modo dichiarativo, forniremo un'API imperativa per questo. (e si collegano ai riferimenti qui)

Ciò significa che il team di React suggerisce di evitare refse utilizzare state / propsper tutto ciò che può essere fatto in modo reattivo / dichiarativo.

@Tyler McGinnis ha fornito un'ottima risposta , affermando anche questo

Il modo corretto (e molto utile) di usare i ref è quando li usi per ottenere un valore dal DOM ...

Anche se puoi farlo, lavorerai contro la filosofia di React. Se hai un valore in un input, sicuramente viene da state / props. Per mantenere il codice coerente e prevedibile, dovresti attenersi anche a state / propsquesto. Riconosco il fatto che a refsvolte ti dà la soluzione più rapida, quindi se fai una prova di concetto, veloce e sporco è accettabile.

Questo ci lascia con diversi casi d'uso concreti perrefs

Gestione dello stato attivo, della selezione del testo o della riproduzione multimediale. Attivazione di animazioni imperative. Integrazione con librerie DOM di terze parti.


5

Questo post è vecchio.

Condividerò la mia piccola esperienza su un caso in materia.

Stavo lavorando su un grande componente (414 linee) con molti input "dinamici" e molti dati memorizzati nella cache coinvolti. (Non sto lavorando da solo sulla pagina, ei miei sensi mi dicono che la struttura del codice probabilmente potrebbe essere suddivisa meglio, ma non è questo il punto (beh, potrebbe essere ma ci sto lavorando)

Ho prima lavorato con lo stato per gestire i valori degli input:

  const [inputsValues, setInputsValues] = useState([])
  const setInputValue = (id, value) => {
    const arr = [...inputsValues]
    arr[id] = value
    setInputsValues(arr)
  }

e ovviamente negli input:

value={inputsValues[id] || ''}
onChange={event => setInputValue(id, event.target.value)}

Il rendering era così pesante che la modifica dell'input era discontinua come **** (non cercare di tenere premuto il tasto, il testo appariva solo dopo una pausa)

Ero sicuro di poterlo evitare usando refs.

è finito così:

  const inputsRef = useRef([])

e negli ingressi:

ref={input => (inputsRef.current[id] = input)}

[bene nel mio caso l'input era Material-UI TextField quindi era:

inputRef={input => (inputsRef.current[id] = input)}

]

Grazie a questo, non vi è alcun rendering, l'input è fluido, la funzionalità funziona allo stesso modo. Risparmierà cicli e calcoli, quindi anche energia. Fallo per la terra x)

La mia conclusione: useRef per il valore di input può anche essere necessario.

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.