Como posso redefinir um componente de reação, incluindo todos os estados transitivamente alcançáveis?

93

Ocasionalmente, tenho componentes de reação que são conceitualmente ativos e que desejo redefinir. O comportamento ideal seria equivalente a remover o componente antigo e reler um novo componente original.

React fornece um método setStateque permite definir o próprio estado explícito dos componentes, mas que exclui o estado implícito, como o foco do navegador e o estado do formulário, e também exclui o estado de seus filhos. Pegar todo esse estado indireto pode ser uma tarefa complicada, e eu prefiro resolvê-lo rigorosa e completamente, em vez de jogar o golpe-a-toupeira a cada novo estado surpreendente.

Existe uma API ou padrão para fazer isso?

Edit: Eu fiz um exemplo trivial demonstrando a this.replaceState(this.getInitialState())abordagem e contrastando-o com a this.setState(this.getInitialState())abordagem: jsfiddle - replaceStateé mais robusto.

Eamon Nerbonne
fonte

Respostas:

206

Para garantir que o estado implícito do navegador que você mencionou e o estado dos filhos sejam redefinidos, você pode adicionar um keyatributo ao componente de nível raiz retornado por render; quando ele muda, esse componente é jogado fora e criado do zero.

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

Não há atalho para redefinir o estado local do componente individual.

Sophie Alpert
fonte
1
Apenas por curiosidade, quando você diz 'o componente será jogado fora e criado do zero', você quer dizer que o DOM virtual será jogado fora e criado do zero, mas as atualizações do DOM ainda acontecerão com base no algoritmo diff?
nimgrg
1
@nimgrg Não, os elementos DOM reais também serão recriados. (O DOM virtual já foi recriado em cada renderização, então se você quiser manter o DOM real, simplesmente não defina um keyneste caso.)
Sophie Alpert
3
@EamonNerbonne No React 0.8, as chaves são usadas apenas para distinguir entre elementos irmãos em uma matriz e foram ignoradas na raiz. Consulte github.com/facebook/react/issues/590 .
Sophie Alpert
Observe replaceState()e getInitialState()estão ambos obsoletos nas novas reactjs de estilo de classe ES6. Use em seu this.setState(this._getInitialState())lugar. Além disso, você não pode nomear sua própria função inicializadora de estado getInitialState()- react lança um aviso - chame-a de qualquer outra coisa e faça explicitamente state = this._getInitialState()no nível superior da classe.
thom_nic
2
Existe uma maneira de fazer isso de dentro de um componente sem exigir que o lado de fora passe um suporte de chave?
trusktr
14

Você deve realmente evitar replaceStatee usar em seu setStatelugar.

Os documentos dizem que replaceState"pode ​​ser totalmente removido em uma versão futura do React." Eu acho que definitivamente será removido porque replaceStaterealmente não combina com a filosofia do React. Facilita fazer um componente React começar a parecer uma espécie de faca suíça. Isso prejudica o crescimento natural de um componente React, tornando-se menor e mais específico.

No React, se você tem que errar na generalização ou na especialização: busque a especialização. Como corolário, a árvore de estado para o seu componente deve ter uma certa parcimônia (é bom quebrar essa regra de bom gosto se você estiver construindo um produto novo em folha).

De qualquer forma, é assim que você faz. Semelhante à resposta de Ben (aceita) acima, mas assim:

this.setState(this.getInitialState());

Além disso (como Ben também disse), para redefinir o "estado do navegador", você precisa remover esse nó DOM. Aproveite o poder do vdom e use um novo keysuporte para esse componente. A nova renderização substituirá esse componente no atacado.

Referência: https://facebook.github.io/react/docs/component-api.html#replacestate

wle8300
fonte
2
Isso não funcionará se o estado atual incluir quaisquer propriedades que não estejam no estado inicial. Embora eu não acredite que esse seja um ótimo "estado" de coisas, a menos que você possa de alguma forma evitá-lo, eu gostaria de evitar uma quebra surpreendente. Eu ficaria bem com uma reinicialização que falha em alguns casos, mas não uma reinicialização que corrompe sutilmente o estado.
Eamon Nerbonne
Não tenho certeza se estou entendendo. Você está dizendo que isso não é equivalente a this.replaceState? Porque é.
wle8300
1
@ williamle8300 Eles não seriam equivalentes se uma nova propriedade fosse adicionada ao estado em algum lugar no ciclo de vida do componente entre chamadas para getInitialState()(digamos, em um manipulador onClick). Nesse caso, essa nova propriedade ainda estaria presente após o 2º getInitialState().
Graham
@Graham Eu acredito que é uma má prática introduzir um novo estado para um componente que ainda não foi declarado em getInitialState. Muito parecido com declarar todas as suas variáveis ​​em uma função JS no topo da função. Nota lateral: a solução de Ben Alpert é quase idêntica à minha ... e ele é um mantenedor oficial do React!
wle8300
1
Infelizmente, a função getInitialState () e, portanto, a resposta, está obsoleta. Uma atualização seria bom.
Martin R.
8

Adicionar um keyatributo ao elemento que você precisa reinicializar irá recarregá-lo toda vez que propsou se stateassociar ao elemento mudar.

key = {new Date (). getTime ()}

Aqui está um exemplo:

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>
  );
}
jsbisht
fonte
1
Ou você pode adicionar uma variável de contador inteiro simples como chave
minimalista de