javascript - event - Reaccionar: cambiar el estado sin usar setState: ¿debe evitarlo?
pushstate w3schools (4)
Personalmente, no siempre sigo la regla, si realmente entiendes lo que estás tratando de hacer, entonces no creo que sea un problema.
var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});
En este caso, mutar de estado y llamar al setState
nuevo así está bien
this.state.data[index].is_collected = !this.state.data[index].is_collected;
this.setState({data: this.state.data});
La razón por la que debe evitar la mutación de su estado es que si tiene una referencia a this.state.data
y llama a setState
varias veces, puede perder sus datos:
const myData = this.state.data
myData[0] = ''foo''
this.setState({ data: myData })
// do something...
// ...
const someNewData = someFunc()
this.setState({ data: someNewData })
myData[1] = ''bar'' // myData is still referencing to the old state
this.setState({ data: myData }) // you lose everything of `someNewData`
Si realmente te preocupas por esto, solo tienes que ir por inmutable.js
Mi código funciona, pero tengo una pregunta sobre las mejores prácticas: tengo una matriz de objetos en el estado y una interacción del usuario cambiará el valor de un objeto a la vez. Por lo que sé, se supone que no debo cambiar el estado directamente, siempre debería usar setState
. Si quiero evitar eso a cualquier precio, haré una clonación profunda de la matriz por iteración, y cambiaré el clon. Luego establece el estado en el clon. En mi opinión, evitar cambiar el estado que voy a cambiar más tarde de todos modos solo está disminuyendo mi rendimiento.
Versión detallada: this.state.data es una matriz de objetos. Representa una lista de temas en un foro, y un botón de Favoritos se activará al llamar a clickCollect()
. Como tengo una matriz en el estado, cuando cambio la propiedad is_collected de un elemento, necesito crear una copia de la matriz con la que trabajar, y después de cambiar al nuevo valor, puedo establecerlo en el estado.
var data = this.state.data.slice(0); data[index].is_collected = !data[index].is_collected; this.setState({data: data});
var data = this.state.data
: Esto copiaría el puntero a la matriz y push (), shift (), etc. alteraría el estado directamente. Ambos data
y this.state.data
se verán afectados.
var data = this.state.data.slice(0)
: Esto hace que un clon superficial, push y shift no cambie el estado, pero en mi clon todavía tengo punteros a los elementos de la matriz del estado. Entonces, si cambio los data[0].is_collected
, this.state.data[0].is_collected
se cambia también. Esto sucede antes de llamar a setState()
.
Normalmente debería hacer:
var data = []; for (var i in this.state.data) { data.push(this.state.data[i]); }
Luego cambio el valor en el índice, configurándolo en verdadero cuando es falso o falso cuando es verdadero:
data[index].is_collected = !data[index].is_collected;
Y cambiar el estado:
this.setState({data: data});
Considere que mi matriz es relativamente grande o enormemente grande, supongo que esta iteración reducirá el rendimiento de mi aplicación. Pagaría ese costo si supiera que es el camino correcto por cualquier motivo. Sin embargo, en esta función ( clickCollect
) siempre establezco el nuevo valor en el estado, no estoy esperando una respuesta falsa de la API que diga que deje de hacer el cambio. En todos los casos, el nuevo valor ingresará en el estado. Prácticamente llamo a setState
solo para que la UI vuelva a renderizar. Entonces las preguntas son:
- ¿Tengo que crear el clon profundo en este caso? (
for var i in ...
) - Si no, ¿tiene sentido hacer un clon superficial (
.slice(0)
) si mi matriz contiene objetos? Los cambios se están realizando en los objetos dentro de la matriz, por lo que el clon superficial aún cambia mi estado, al igual que haría una copia (data = this.state.data
).
Mi código se simplifica y las llamadas API se recortan por simplicidad.
Esta es una pregunta para principiantes, por lo que un enfoque totalmente diferente también es bienvenido. O enlaces a otras preguntas y respuestas
import React from ''react''; var ForumList = React.createClass({ render: function() { return <div className="section-inner"> {this.state.data.map(this.eachBox)} </div> }, eachBox: function(box, i) { return <div key={i} className="box-door"> <div className={"favorite " + (box.is_collected ? "on" : "off")} onTouchStart={this.clickCollect.bind(null, i)}> {box.id} </div> </div> }, getInitialState: function() { return {data: [ { id: 47, is_collected: false }, { id: 23, is_collected: false }, { id: 5, is_collected: true } ]}; }, clickCollect: function(index) { var data = this.state.data.slice(0); data[index].is_collected = !data[index].is_collected; this.setState({data: data}); } }); module.exports = ForumList;
Si desea seguir las mejores prácticas de reacción, debe hacer una copia superficial de toda su matriz, cuando cambie cualquier propiedad. Mire en la implementación de la biblioteca "inmutable".
Pero, desde mi experiencia, y desde mi opinión, debe llamarse al método setState
si tiene implementaciones "shouldCompomenentUpdate". Si crees que tu copia poco profunda consumirá muchos más recursos y luego reaccionarás a los controles virtuales, puedes hacer esto:
this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();
Si entendí bien tu pregunta, tienes una matriz de objetos y cuando cambia la propiedad de un solo objeto en la matriz,
- Crea un clon profundo de la matriz y pasa a setState
- Crea un clon superficial y pasa a setState
Acabo de consultar con la aplicación redux
sample todo y, en el caso de una sola propiedad de un objeto, debe crear una copia nueva de ese único objeto, no toda la matriz. Te recomiendo que leas acerca de redux
y si es posible redux
para administrar el estado de tu aplicación.
Silenciar el estado rompe directamente el principio principal del flujo de datos de React (que se hace que sea unidireccional), lo que hace que su aplicación sea muy frágil y básicamente ignora todo el ciclo de vida de los componentes.
Entonces, aunque nada realmente le impide mutar el estado del componente sin setState ({}), tendría que evitarlo a toda costa si realmente quiere aprovechar Reaccionar, de lo contrario estaría superando una de las funcionalidades principales de la biblioteca.