Você pode forçar um componente React a fazer o rerender sem chamar setState?

Eu tenho um object externo (para o componente), observável que eu quero ouvir por mudanças. Quando o object é atualizado, ele emite events de alteração e, em seguida, desejo remitentar o componente quando for detectada qualquer alteração.

Com um React.render nível React.render isso foi possível, mas dentro de um componente ele não funciona (o que faz algum sentido, já que o método render apenas retorna um object).

Aqui está um exemplo de código:

 export default class MyComponent extends React.Component { handleButtonClick() { this.render(); } render() { return ( 
{Math.random()}
) } }

Clicar no botão chama internamente this.render() , mas não é isso que realmente faz a renderização acontecer (você pode ver isso em ação porque o texto criado por {Math.random()} não muda). No entanto, se eu simplesmente chamar this.setState() vez de this.render() , ele funciona bem.

Então, eu acho que a minha pergunta é: os componentes React precisam ter estado para renderizar novamente? Existe uma maneira de forçar o componente a atualizar sob demanda sem alterar o estado?

No seu componente, você pode chamar this.forceUpdate() para forçar um rerender.

Documentação: https://facebook.github.io/react/docs/component-api.html

forceUpdate deve ser evitado porque se desvia de uma mentalidade React. Os documentos do React citam um exemplo de quando forceUpdate pode ser usado:

Por padrão, quando o estado ou os suportes do seu componente mudam, seu componente será renderizado novamente. No entanto, se elas forem alteradas implicitamente (por exemplo: dados profundos em um object são alterados sem alterar o object em si) ou se o método render () depender de outros dados, você poderá dizer ao React que precisa executar render () chamando forçar atualização().

No entanto, gostaria de propor a ideia de que, mesmo com objects profundamente nesteds, o forceUpdate é desnecessário. Ao usar uma fonte de dados imutável, as alterações de rastreamento se tornam baratas; uma alteração sempre resultará em um novo object, portanto, precisamos apenas verificar se a referência ao object foi alterada. Você pode usar a biblioteca Immutable JS para implementar objects de dados imutáveis ​​no seu aplicativo.

Normalmente você deve tentar evitar todos os usos de forceUpdate () e somente ler a partir dele.props e this.state em render (). Isso torna seu componente “puro” e sua aplicação muito mais simples e eficiente. https://facebook.github.io/react/docs/component-api.html#forceupdate

A alteração da chave do elemento que você deseja renderizar novamente funcionará. Defina a chave prop em seu elemento via estado e, em seguida, quando quiser atualizar o estado configurado para ter uma nova chave.

  

Então ocorre uma mudança e você redefine a chave

 this.setState({ key: Math.random() }); 

Quero observar que isso replaceá o elemento em que a chave está sendo alterada. Um exemplo de como isso pode ser útil é quando você tem um campo de input de arquivo que deseja redefinir após o upload de uma imagem.

Embora a verdadeira resposta à pergunta do OP seja forceUpdate() , achei essa solução útil em diferentes situações. Também quero observar que, se você se encontrar usando o forceUpdate poderá rever seu código e ver se há outra maneira de fazer as coisas.

Na verdade, forceUpdate() é a única solução correta, pois setState() pode não acionar uma re-renderização se uma lógica adicional for implementada em shouldComponentUpdate() ou quando ela simplesmente retornar false .

forçar atualização()

Chamar forceUpdate() fará com que render() seja chamado no componente, pulando shouldComponentUpdate() . Mais…

setState ()

setState() sempre triggersrá uma renderização novamente, a menos que a lógica de renderização condicional seja implementada em shouldComponentUpdate() . Mais…


forceUpdate() pode ser chamado de dentro de seu componente por this.forceUpdate()

Quando você deseja que dois componentes React se comuniquem, que não estão vinculados a um relacionamento (pai-filho), é aconselhável usar o Flux ou arquiteturas semelhantes.

O que você deseja fazer é ouvir as alterações do armazenamento de componente observável , que contém o modelo e sua interface, e salvar os dados que fazem com que a renderização seja alterada como state em MyComponent . Quando a loja envia os novos dados, você altera o estado do seu componente, o que aciona automaticamente a renderização.

Normalmente você deve tentar evitar o uso de forceUpdate() . Da documentação:

Normalmente você deve tentar evitar todos os usos de forceUpdate () e somente ler a partir dele.props e this.state em render (). Isso torna sua aplicação muito mais simples e eficiente

forceUpdate fazendo o seguinte

WRONG WAY : não use o índice como chave

 this.state.rows.map((item, index) =>  ) 

CORRETO WAY : Use os dados id como chave, pode ser algum guia etc

 this.state.rows.map((item) =>  ) 

Então, fazendo essa melhoria de código, seu componente será ÚNICO e render naturalmente

Então, eu acho que a minha pergunta é: os componentes React precisam ter estado para renderizar novamente? Existe uma maneira de forçar o componente a atualizar sob demanda sem alterar o estado?

As outras respostas tentaram ilustrar como você poderia, mas o ponto é que você não deveria . Até mesmo a solução hacky de mudar a chave não tem sentido. O poder do React é abrir mão do controle de gerenciar manualmente quando algo deve render e, em vez disso, apenas se preocupar com o modo como algo deve mapear as inputs. Em seguida, fornecer stream de insumos.

Se você precisar forçar manualmente a re-renderização, você quase certamente não estará fazendo algo certo.

Nenhum estado se preocupa em re-render

Eu estava armazenando meus dados no estado muito quando comecei a usar reagir pensando que eu tinha que fazer isso para renderização. Isso foi muito errado. Acontece que você pode até mesmo evitar fazer coisas complicadas, como Flux e Redux, também, se você criar algumas lojas imutáveis ​​simples.

Classe BaseView para herdar e manipular atualizações de loja

 import React, { Component } from 'react'; import { View } from 'react-native'; export default class BaseView extends Component { constructor(props) { super(props) this.state = {} } onChange = () => { this.setState(this.state) // dumb easy: triggers render } componentWillMount = () => { this.stores && this.stores.forEach(store => { // each store has a common change event to subscribe to store.on('change', this.onChange) }) } componentWillUnmount = () => { this.stores && this.stores.forEach(store => { store.off('change', this.onChange) }) } } 

Como usar

 import React, { Component } from 'react' import { View, Text } from 'react-native' import BaseView from './BaseView' import myStore from './myStore' class MyView extends BaseView { contructor(props) { super(props) this.stores = [myStore] } render() { return ( { myStore.update({ message: 'hi' + Date.now() }) }}>{myStore.get().message}) } } 

Exemplo de class de loja simples

 var ee = require('event-emitter') export default class Store { constructor() { this._data = {} this._eventEmitter = ee({}) } get() { return {...this._data} // immutable } update(newData) { this._data = {..._this.data, ...newData} this._eventEmitter.emit('change') } on(ev, fn) { this._eventEmitter.on(ev, fn) } off(ev, fn) { this._eventEmitter.off(ev, fn) } } 

Armazenar instância como singleton

 import Store from './Store' const myStore = new Store() export default myStore 

Eu acho que isso acaba com todas as estruturas de crufty necessárias para aplicativos menores. Use o redux para médios e grandes, acoplado ao Immutable.js.

Podemos usar this.forceUpdate () como abaixo.

  class MyComponent extends React.Component { handleButtonClick = ()=>{ this.forceUpdate(); } render() { return ( 
{Math.random()}
) } } ReactDOM.render( , mountNode);

A parte do elemento ‘Math.random’ no DOM só é atualizada mesmo se você usar o setState para renderizar novamente o componente.

Todas as respostas aqui estão corretas, complementando a questão para a compreensão … como sabemos, renderizar novamente um componente sem usar setState ({}) é usando o forceUpdate ().

O código acima é executado com setState como abaixo.

  class MyComponent extends React.Component { handleButtonClick = ()=>{ this.setState({ }); } render() { return ( 
{Math.random()}
) } } ReactDOM.render( , mountNode);

Você poderia fazer isso de várias maneiras:

1. Use o método forceUpdate() :

Existem alguns problemas que podem ocorrer quando se usa o método forceUpdate() . Um exemplo é que ele ignora o método shouldComponentUpdate() e renderiza novamente a exibição, independentemente de se shouldComponentUpdate() retornar false. Por causa disso, o uso de forceUpdate () deve ser evitado sempre que possível.

2. Passando this.state para o método setState()

A seguinte linha de código supera o problema com o exemplo anterior:

 this.setState(this.state); 

Realmente tudo isso está fazendo sobrescrevendo o estado atual com o estado atual que aciona uma nova renderização. Isso ainda não é necessariamente a melhor maneira de fazer as coisas, mas supera algumas das falhas que você pode encontrar usando o método forceUpdate ().

Eu achei melhor evitar forceUpdate (). Uma maneira de forçar a re-renderização é adicionar dependência de render () em uma variável externa temporária e alterar o valor dessa variável como e quando necessário.

Aqui está um exemplo de código:

 class Example extends Component{ constructor(props){ this.state = {temp:0}; this.forceChange = this.forceChange.bind(this); } forceChange(){ this.setState(prevState => ({ temp: prevState.temp++ })); } render(){ return( 
{this.state.temp &&
... add code here ...
}
) } }

Chame this.forceChange () quando precisar forçar a re-renderização.

ES6 – Estou incluindo um exemplo, que foi útil para mim:

Em um “short if statement” você pode passar a function vazia assim:

 isReady ? ()=>{} : onClick 

Esta parece ser a abordagem mais curta.

 ()=>{} 

Outra maneira é chamar setState e preservar estado:

this.setState(prevState=>({...prevState});

forceUpdate(); método funcionará, mas é aconselhável usar setState();