Usar state ou refs nos componentes do formulário React.js?

116

Estou começando com React.js e quero fazer um formulário simples, mas na documentação, encontrei duas maneiras de fazê-lo.

O primeiro está 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 o segundo está usando o estado dentro do 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: ''
  });
});

Não consigo ver os prós e os contras das duas alternativas, se houver alguma. Obrigado.

gabrielgiussi
fonte
Estou faltando alguma coisa aqui? Por que você não usa o objeto de evento para obter os valores do formulário? Essa parece ser a única razão para usar um formulário aqui em primeiro lugar. Se você não estiver usando o comportamento de envio padrão e tiver referências nas entradas, não será necessário envolvê-las em um formulário.
NectarSoft

Respostas:

143

A versão curta: evite refs.


Eles são ruins para a manutenção e perdem muito da simplicidade que a renderização do modelo WYSIWYG oferece.

Você tem um formulário. Você precisa adicionar um botão que redefina o formulário.

  • refs:
    • manipular o DOM
    • render descreve como o formulário parecia 3 minutos atrás
  • Estado
    • setState
    • render descreve a aparência do formulário

Você tem um campo de número CCV em uma entrada e alguns outros campos em seu aplicativo que são números. Agora você precisa obrigar o usuário a inserir apenas números.

  • refs:
    • adicione um manipulador onChange (não estamos usando refs para evitar isso?)
    • manipular dom em onChange se não for um número
  • Estado
    • você já tem um manipulador onChange
    • adicione uma instrução if, se for inválida, não faça nada
    • render só é chamado se for produzir um resultado diferente

Eh, esquece, o PM quer que façamos apenas uma sombra de caixa vermelha se for inválida.

  • refs:
    • make onChange handler apenas chama forceUpdate ou algo assim?
    • fazer a saída de renderização baseada em ... hein?
    • onde obtemos o valor para validar no render?
    • manipular manualmente a propriedade dom className de um elemento?
    • estou perdido
    • reescrever sem refs?
    • ler do dom no render se estivermos montados, de outra forma, assumir válido?
  • Estado:
    • remova a declaração if
    • fazer render validar com base em this.state

Precisamos devolver o controle aos pais. Os dados agora estão em adereços e precisamos reagir às mudanças.

  • refs:
    • implementar componentDidMount, componentWillUpdate e componentDidUpdate
    • comparar manualmente os adereços anteriores
    • manipular o dom com o conjunto mínimo de mudanças
    • Ei! estamos implementando react em react ...
    • tem mais, mas meus dedos doem
  • Estado:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

As pessoas pensam que os árbitros são "mais fáceis" do que mantê-los no estado. Isso pode ser verdade nos primeiros 20 minutos, não é verdade na minha experiência depois disso. Coloque-se em posição de dizer "Sim, farei isso em 5 minutos" em vez de "Claro, vou reescrever alguns componentes".

Brigand
fonte
3
Você poderia explicar um pouco mais sobre sed -e 's / this.state / this.props /' 's / handleChange / onChange /' -i form.js?
gabrielgiussi
1
Não, quero dizer mudanças reais no dom. React.findDOMNode(this.refs.foo). Se você mudar, por exemplo, this.refs.foo.props.barnada acontecerá.
Brigand
1
por exemplo, se você <input onChange={this.handleChange} value={this.state.foo} />alterou para <input onChange={this.props.handleChange} value={this.props.foo} />, ou modificou sua (s) função (ões) handleChange para chamar o (s) retorno (ões) de chamada em props. De qualquer forma, são algumas pequenas mudanças óbvias.
Brigand
4
Não tenho certeza se sou o único a achar sua resposta um pouco confusa. Você poderia mostrar alguns exemplos de código tornando seus pontos mais claros?
Rishabh
2
Ter mais de 50 entradas em uma tela e renderizar cada uma em qualquer mudança de estado é indesejável. Componentizar cada inputcampo onde cada um mantém seu próprio estado é ideal. Em algum ponto, precisamos reconciliar esses vários estados independentes com algum modelo maior. Talvez tenhamos um salvamento automático em um cronômetro, ou apenas economizamos. componentWillUnmountÉ aqui que eu acho o refsideal, durante a reconciliação, extraímos o statevalor de cada um ref, e ninguém é mais sábio. Eu concordo na maioria dos casos stateé a resposta, mas com um grande número de inputs, utilizar um refspadrão adequado é um benefício de desempenho
lux
105

Eu vi algumas pessoas citarem a resposta acima como uma razão para "nunca usar refs" e eu quero dar minha opinião (assim como alguns outros desenvolvedores do React com quem conversei).

O sentimento "não use refs" está correto ao falar sobre como usá-los para instâncias de componente. Ou seja, você não deve usar refs como uma forma de obter instâncias de componentes e chamar métodos neles. Esta é a maneira incorreta de usar os árbitros e é quando os árbitros vão para o sul rapidamente.

A maneira correta (e muito útil) de usar refs é quando você os usa para obter algum valor do DOM. Por exemplo, se você tem um campo de entrada anexando um ref a essa entrada, então pegar o valor mais tarde por meio do ref está ótimo. Sem essa maneira, você precisa passar por um processo razoavelmente orquestrado para manter seu campo de entrada atualizado com o estado local ou com o armazenamento de fluxo - o que parece desnecessário.

Edição de 2019: Olá amigos do futuro. Além do que mencionei há alguns anos ^, com React Hooks, refs também são uma ótima maneira de manter o controle de dados entre renderizações e não estão limitados a apenas capturar nós DOM.

Tyler McGinnis
fonte
3
Seu último parágrafo faz todo o sentido, mas você pode esclarecer o segundo parágrafo? Qual é um exemplo concreto de pegar uma instância do componente e chamar um método que seria considerado incorreto?
Danny Libin
2
Eu concordo com isso. Eu uso refs a menos / até que eu precise fazer a validação ou manipulação do valor de um campo. Se eu precisar validar a alteração ou alterar os valores programaticamente, uso o estado.
Christopher Davies
1
Eu também concordo com isso. Durante uma fase de descoberta, aproximei-me propositalmente de uma tela com um grande número de entradas com ingenuidade. Todos os valores de entrada armazenados em um mapa (no estado) codificados por id. Desnecessário dizer que o desempenho sofreu desde a configuração do estado e renderização de mais de 50 entradas (alguns materiais-ui, que eram pesados!) Em pequenas alterações da interface do usuário como um clique na caixa de seleção não era o ideal. Componentizar cada entrada que pode manter seu próprio estado parecia a abordagem adequada. Se a reconciliação for necessária, apenas examine o refse obtenha o valor do estado. Na verdade, parece um padrão muito bom.
lux
2
Eu concordo completamente. A resposta aceita é muito vaga em minha opinião.
James Wright de
Concordo. Ao projetar um componente Form geral, isso traz à luz os pontos problemáticos dos componentes controlados e do gerenciamento de foco, tratamento de erros, etc. Não é possível ter uma arquitetura limpa de fato. Fale comigo se necessário. Estou movendo meus componentes para os árbitros.
kushalvm
6

TL; DR De modo geral, refsvá contra a filosofia declarativa do React , então você deve usá-los como último recurso. Use state / propssempre que possível.


Para entender onde você usa refsvs state / props, vamos dar uma olhada em alguns dos princípios de design que o React segue.

Documentação por React sobrerefs

Evite usar refs para qualquer coisa que possa ser feita declarativamente.

Princípios de design de Per React sobre escotilhas de fuga

Se algum padrão útil para a construção de aplicativos for difícil de expressar de forma declarativa, forneceremos uma API obrigatória para ele. (e eles têm um link para os refs aqui)

O que significa que a equipe do React sugere evitar refse usar state / propspara qualquer coisa que possa ser feita de forma reativa / declarativa.

@Tyler McGinnis deu uma resposta muito boa , afirmando também que

A maneira correta (e muito útil) de usar refs é quando você os usa para obter algum valor do DOM ...

Embora você possa fazer isso, estará trabalhando contra a filosofia do React. Se você tem valor em uma entrada, certamente vem de state / props. Para manter o código consistente e previsível, você também deve se limitar a state / propsisso. Eu reconheço o fato de que refsàs vezes te dá a solução mais rápida, então se você fizer uma prova de conceito, rápido e sujo é aceitável.

Isso nos deixa com vários casos de uso concretos pararefs

Gerenciando foco, seleção de texto ou reprodução de mídia. Ativando animações imperativas. Integração com bibliotecas DOM de terceiros.

Lyubomir
fonte
5

Esta postagem é antiga.

Vou compartilhar minha pequena experiência em um caso sobre esse assunto.

Eu estava trabalhando em um grande componente (414 linhas) com muitas entradas 'dinâmicas' e muitos dados em cache envolvidos. (Não estou trabalhando sozinho na página e meus sentidos me dizem que a estrutura do código provavelmente poderia ser dividida melhor, mas não é o ponto (bem, poderia ser, mas estou lidando com isso)

Trabalhei primeiro com o estado para lidar com os valores das entradas:

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

e, claro, nas entradas:

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

A renderização era tão pesada que a alteração da entrada era instável como **** (não tente manter a tecla pressionada, o texto só apareceria após uma pausa)

Eu tinha certeza de que poderia evitar isso usando refs.

acabou assim:

  const inputsRef = useRef([])

e nas entradas:

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

[bem, no meu caso, a entrada era Material-UI TextField, então era:

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

]

Graças a isso, não há renegociação, a entrada é suave, a funcionalidade funciona da mesma forma. Isso vai economizar ciclos e cálculos, portanto, energia também. Faça isso pela terra x)

Minha conclusão: useRef para o valor das entradas pode até ser necessário.

Kaphar
fonte