Em aplicações React, frequentemente encontramos situações onde é necessário manter o estado de componentes específicos mesmo quando não estão visíveis. Uma abordagem comum envolve utilizar o componente Route com o método children para alternar a exibição entre block e none. No entanto, essa técnica apresanta limitações, pois depende do sistema de rotas e só é aplicável a páginas inteiras, não a componentes individuais.
Este artigo apresenta uma alternativa que permite preservar o estado de componentes durante seu ciclo de vida, mesmo quando são temporariamente removidos da árvore DOM.
Conceito Fundamental
A solução proposta baseia-se em capturar o estado de um componente antes de seu desmontagem e restaurá-lo quando o componente for re-renderizado. Essa abordagem garante que o componente retome exatamente onde parou, mantendo toda a sua integridade de estado.
Implementação
Vamos criar um Higher-Order Component (HOC) que encapsulará nossa lógica de cache. Este HOC será responsável por:
- Interceptar o ciclo de vida do componente encapsulado
- Armazenar o estado antes do desmontagem
- Restaurar o estado durante a montagem subsequente
Código do HOC de Cache
const preservarEstado = (ComponenteAlvo) => {
const armazenamentoCache = {};
return class ComponentePreservado extends React.Component {
constructor(props) {
super(props);
this.referenciaInterna = null;
}
componentDidMount() {
if (this.referenciaInterna) {
const estadoAtual = this.referenciaInterna.state;
this.referenciaInterna.setState({
...estadoAtual,
...armazenamentoCache[ComponenteAlvo.name]
});
}
}
componentWillUnmount() {
if (this.referenciaInterna) {
armazenamentoCache[ComponenteAlvo.name] = {
...this.referenciaInterna.state
};
}
}
render() {
return (
<ComponenteAlvo
ref={ref => this.referenciaInterna = ref}
{...this.props}
/>
);
}
};
};
export default preservarEstado;
Exemplo de Uso
Vamos criar um componente contador que demonstrará como o estado é preservado:
import React from 'react';
import preservarEstado from './preservarEstado';
class ContadorExemplo extends React.Component {
estadoInicial = {
valor: 0,
historico: []
};
constructor(props) {
super(props);
this.state = this.estadoInicial;
}
incrementar = () => {
const novoValor = this.state.valor + 1;
this.setState({
valor: novoValor,
historico: [...this.state.historico, novoValor]
});
};
render() {
const { valor, historico } = this.state;
return (
<div className="componente-contador">
<h3>Componente com Cache de Estado</h3>
<p>Valor atual: {valor}</p>
<button onClick={this.incrementar}>
Incrementar
</button>
<div>
<h4>Histórico:</h4>
<ul>
{historico.map((item, indice) => (
<li key={indice}>{item}</li>
))}
</ul>
</div>
</div>
);
}
}
export default preservarEstado(ContadorExemplo);
Limitações e Considerações
É importante notar que esta implementação atualmente só funciona com componentes baseados em classes (herdados de React.Component). Componentes funcionais que utilizam Hooks não são suportados, pois:
- O estado em componentes funcionais é gerenciado internamente pelo React
- Não existe uma referência direta ao componente para acessar seu estado
- A API de Hooks não expõe métodos para capturar todo o estado de uma vez
Para componentes funcionais, seria necessário uma abordagem completamente diferente, possivelmente envolvendo o uso de useRef e useState de forma personalizada.
Utilização Prática
Esta solução já está disponível como pacote npm. Para utilizá-la em seu projeto:
npm install react-component-cache --save
Após a instalação, importe o HOC e aplique-o aos componentes que precisam de cache de estado:
import React from 'react';
import withCache from 'react-component-cache';
class MeuComponente extends React.Component {
// ... implementação do componente
}
export default withCache(MeuComponente);