¿Cómo puedo restablecer un componente de reacción, incluidos todos los estados accesibles transitivamente?

93

Ocasionalmente tengo componentes de reacción que tienen un estado conceptual que quiero restablecer. El comportamiento ideal sería equivalente a eliminar el componente antiguo y leer un componente nuevo y prístino.

React proporciona un método setStateque permite establecer el propio estado explícito de los componentes, pero que excluye el estado implícito, como el enfoque del navegador y el estado del formulario, y también excluye el estado de sus hijos. Capturar todo ese estado indirecto puede ser una tarea complicada, y prefiero resolverlo de manera rigurosa y completa en lugar de jugar a whack-a-mole con cada nuevo estado sorprendente.

¿Existe una API o patrón para hacer esto?

Editar: hice un ejemplo trivial que demuestra el this.replaceState(this.getInitialState())enfoque y lo contrasta con el this.setState(this.getInitialState())enfoque: jsfiddle - replaceStatees más robusto.

Eamon Nerbonne
fuente

Respuestas:

206

Para asegurarse de que se restablezca el estado implícito del navegador que menciona y el estado de los hijos, puede agregar un keyatributo al componente de nivel raíz devuelto por render; cuando cambie, ese componente se desechará y se creará desde cero.

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

No hay atajos para restablecer el estado local del componente individual.

Sophie Alpert
fuente
1
Solo por curiosidad, cuando dice 'el componente se desechará y se creará desde cero', ¿quiere decir que el DOM virtual se desechará y se creará desde cero, pero las actualizaciones del DOM seguirán ocurriendo según el algoritmo diff?
nimgrg
1
@nimgrg No, los elementos DOM reales también se volverán a crear. (El DOM virtual ya se recrea en cada renderizado, por lo que si desea mantener el DOM real, simplemente no configure un keyen este caso).
Sophie Alpert
3
@EamonNerbonne En React 0.8, las claves solo se usan para distinguir entre elementos hermanos en una matriz y se ignoraron en la raíz. Consulte github.com/facebook/react/issues/590 .
Sophie Alpert
Nota replaceState()y getInitialState()son a la vez obsoleto en los nuevos reactjs ES6-estilo de clase. Úselo en su this.setState(this._getInitialState())lugar. Además, no puede nombrar su propia función de inicializador de estado getInitialState(): reaccionar arroja una advertencia, llámelo de cualquier otra manera y hágalo explícitamente state = this._getInitialState()en el nivel superior de la clase.
thom_nic
2
¿Hay alguna manera de hacer esto desde el interior de un componente sin requerir que el exterior pase un accesorio clave?
trusktr
14

En realidad, debería evitarlo replaceStatey utilizarlo setStateen su lugar.

Los documentos dicen que replaceState"puede eliminarse por completo en una versión futura de React". Creo que definitivamente se eliminará porque replaceStaterealmente no concuerda con la filosofía de React. Facilita hacer que un componente de React comience a sentirse como un cuchillo suizo. Esto contrarresta el crecimiento natural de un componente de React de volverse más pequeño y más hecho a propósito.

En React, si tiene que pecar de generalización o especialización: apunte a la especialización. Como corolario, el árbol de estado de su componente debe tener cierta parsimonia (sin embargo, está bien romper con buen gusto esta regla si está construyendo un nuevo producto flamante).

De todos modos así es como lo haces. Similar a la respuesta de Ben (aceptada) anterior, pero así:

this.setState(this.getInitialState());

Además (como también dijo Ben) para restablecer el "estado del navegador", debe eliminar ese nodo DOM. Aproveche el poder del vdom y use un nuevo keyaccesorio para ese componente. El nuevo render reemplazará ese componente al por mayor.

Referencia: https://facebook.github.io/react/docs/component-api.html#replacestate

wle8300
fuente
2
Esto no funcionará si el estado actual incluye propiedades que no se encuentran en el estado inicial. Si bien no creo que sea un gran "estado" de cosas en el que estar, a menos que puedas evitarlo de alguna manera, me gustaría evitar una rotura sorprendente. Estaría bien con un reinicio que falla en algunos casos, pero no un reinicio que corrompe sutilmente el estado.
Eamon Nerbonne
No estoy seguro de seguir. ¿Estás diciendo que esto no es equivalente a this.replaceState? Porque lo es.
wle8300
1
@ williamle8300 No serían equivalentes si se agrega una nueva propiedad al estado en algún lugar del ciclo de vida del componente entre llamadas a getInitialState()(digamos, en un controlador onClick). En este caso, esa nueva propiedad aún estaría presente después del 2do getInitialState().
Graham
@Graham Creo que es una mala práctica introducir un nuevo estado en un componente que aún no está declarado en getInitialState. Al igual que declarar todas sus variables en una función JS en la parte superior de la función. Nota al margen: la solución de Ben Alpert es casi idéntica a la mía ... ¡y él es un mantenedor oficial de React!
wle8300
1
Me temo que la función getInitialState () y, por lo tanto, la respuesta, está en desuso. Una actualización estaría bien.
Martin R.
9

Agregar un keyatributo al elemento que necesita reinicializar, lo recargará cada vez que el propso stateasociado al elemento cambie.

clave = {nueva fecha (). getTime ()}

Aquí hay un ejemplo:

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
fuente
1
O puede agregar una variable de contador de enteros simple como clave
minimalista