logo - ReactJS state versus prop
react logo (6)
Creo que estás usando un antipatrón que Facebook ya explicó en este link
Esto es lo que estás encontrando:
React.createClass({
getInitialState: function() {
return { value: { foo: ''bar'' } };
},
onClick: function() {
var value = this.state.value;
value.foo += ''bar''; // ANTI-PATTERN!
this.setState({ value: value });
},
render: function() {
return (
<div>
<InnerComponent value={this.state.value} />
<a onClick={this.onClick}>Click me</a>
</div>
);
}
});
La primera vez que se renderiza el componente interno, tendrá {foo: ''bar''} como valor prop. Si el usuario hace clic en el delimitador, el estado del componente principal se actualizará a {value: {foo: ''barbar''}}, lo que activará el proceso de re-renderización del componente interno, que recibirá {foo: ''barbar''} como el nuevo valor para el apoyo.
El problema es que, dado que el padre y los componentes internos comparten una referencia al mismo objeto, cuando el objeto se muta en la línea 2 de la función onClick, cambiará el apuntalamiento del componente interno. Por lo tanto, cuando se inicie el proceso de re-renderización, y se invoque shouldComponentUpdate, this.props.value.foo será igual a nextProps.value.foo, porque de hecho, this.props.value hace referencia al mismo objeto como nextProps.value.
En consecuencia, dado que perderemos el cambio en el puntal y cortocircuitará el proceso de re-renderización, la interfaz de usuario no se actualizará de ''bar'' a ''barbar''.
Esto puede estar pisando esa línea entre responable y obstinado, pero voy y vengo sobre cómo estructurar un componente ReactJS a medida que la complejidad crece y podría usar alguna dirección.
Viniendo de AngularJS, quiero pasar mi modelo en el componente como una propiedad y hacer que el componente modifique el modelo directamente. ¿O debería dividir el modelo en varias propiedades de state
y volver a compilarlo cuando lo envíe de vuelta? ¿Cuál es la forma ReactJS?
Tome el ejemplo de un editor de publicación de blog. Tratar de modificar el modelo directamente termina pareciéndose a:
var PostEditor = React.createClass({
updateText: function(e) {
var text = e.target.value;
this.props.post.text = text;
this.forceUpdate();
},
render: function() {
return (
<input value={this.props.post.text} onChange={this.updateText}/>
<button onClick={this.props.post.save}/>Save</button>
);
}
});
Lo cual parece estar mal.
¿Es más la forma Reaccionar para hacer que nuestro state
propiedad del modelo de text
, y compilarlo de nuevo en el modelo antes de guardar como:
var PostEditor = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: this.props.post.text
});
},
updateText: function(e) {
this.setState({
text: e.target.value
});
},
savePost: function() {
this.props.post.text = this.state.text;
this.props.post.save();
},
render: function() {
return (
<input value={this.state.text} onChange={this.updateText}/>
<button onClick={this.savePost}/>Save</button>
);
}
});
Esto no requiere una llamada a this.forceUpdate()
, pero a medida que el modelo crece, (una publicación puede tener un autor, tema, etiquetas, comentarios, calificaciones, etc.), el componente comienza a volverse realmente complicado.
¿Es el primer método con ReactLink el camino a seguir?
De React doc
Los accesorios son inmutables: se pasan de los padres y son "propiedad" de los padres. Para implementar interacciones, introducimos el estado mutable al componente. this.state es privado para el componente y se puede cambiar llamando a this.setState (). Cuando se actualiza el estado, el componente se renueva a sí mismo.
Desde TrySpace : cuando props (o state) se actualizan (a través de setProps / setState o parent), el componente también se renueva.
No estoy seguro de si estoy respondiendo su pregunta, pero he descubierto que, especialmente en una aplicación grande / en crecimiento, el patrón Contenedor / Componente funciona increíblemente bien.
Esencialmente tienes dos componentes React:
- un componente de visualización "puro", que trata sobre el estilo y la interacción DOM;
- un componente de contenedor, que trata de acceder / guardar datos externos, gestionar el estado y representar el componente de visualización.
Ejemplo
NB Este ejemplo es probablemente demasiado simple para ilustrar los beneficios de este patrón, ya que es bastante detallado para un caso tan sencillo.
/**
* Container Component
*
* - Manages component state
* - Does plumbing of data fetching/saving
*/
var PostEditorContainer = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: getPostText()
});
},
updateText: function(text) {
this.setState({
text: text
});
},
savePost: function() {
savePostText(this.state.text);
},
render: function() {
return (
<PostEditor
text={this.state.text}
onChange={this.updateText.bind(this)}
onSave={this.savePost.bind(this)}
/>
);
}
});
/**
* Pure Display Component
*
* - Calculates styling based on passed properties
* - Often just a render method
* - Uses methods passed in from container to announce changes
*/
var PostEditor = React.createClass({
render: function() {
return (
<div>
<input type="text" value={this.props.text} onChange={this.props.onChange} />
<button type="button" onClick={this.props.onSave} />
</div>
);
}
});
Beneficios
Al mantener la lógica de visualización y la gestión de datos / estado por separado, tiene un componente de visualización reutilizable que:
- puede repetirse fácilmente con diferentes conjuntos de accesorios usando algo como react-component-playground
- puede envolverse con un contenedor diferente para un comportamiento diferente (o combinarse con otros componentes para construir partes más grandes de su aplicación)
También tiene un componente contenedor que trata con todas las comunicaciones externas. Esto debería facilitar la flexibilidad con respecto a la forma en que accede a sus datos si realiza cambios importantes más adelante *.
Este patrón también hace que escribir e implementar pruebas unitarias sea mucho más directo.
Habiendo repetido varias veces una gran aplicación de React, he descubierto que este patrón mantiene las cosas relativamente indoloras, especialmente cuando tienes componentes más grandes con estilos calculados o complicadas interacciones DOM.
* Lea sobre el patrón de flujo, y eche un vistazo a Marty.js , que inspiró en gran medida esta respuesta (y he estado usando mucho últimamente) Redux (y react-redux ), que implementan este patrón extremadamente bien.
Su segundo enfoque es más parecido. React no se preocupa por los modelos tanto como le importan los valores y cómo fluyen a través de su aplicación. Idealmente, su modelo de publicación se almacenaría en un único componente en la raíz. A continuación, crea componentes secundarios que cada uno consume partes del modelo.
Puede pasar las retrollamadas a los niños que necesitan modificar sus datos y llamarlos desde el componente secundario.
La modificación directa de this.props o this.state no es una buena idea, porque React no podrá retomar los cambios. Eso se debe a que React hace una comparación superficial de su postpro para determinar si ha cambiado.
Hice este jsfiddle para mostrar cómo los datos podrían fluir de un componente externo a uno interno:
http://jsfiddle.net/jxg/M3CLB/
El método handleClick
muestra 3 formas de (im) actualizar correctamente el estado:
var Outer = React.createClass({
getInitialState: function() {
return {data: {value: ''at first, it works''}};
},
handleClick: function () {
// 1. This doesn''t work, render is not triggered.
// Never set state directly because the updated values
// can still be read, which can lead to unexpected behavior.
this.state.data.value = ''but React will never know!'';
// 2. This works, because we use setState
var newData = {value: ''it works 2''};
this.setState({data: newData});
// 3. Alternatively you can use React''s immutability helpers
// to update more complex models.
// Read more: http://facebook.github.io/react/docs/update.html
var newState = React.addons.update(this.state, {
data: {value: {$set: ''it works''}}
});
this.setState(newState);
},
render: function() {
return <Inner data={this.state.data} handleClick={this.handleClick} />;
}
});
Una lectura de Thinking in React
Repasemos cada uno y descubramos cuál es el estado. Simplemente haga tres preguntas sobre cada dato:
- ¿Se transfiere desde un padre a través de accesorios? Si es así, probablemente no sea estado.
¿Cambia con el tiempo? Si no, probablemente no sea estado.
¿Se puede calcular en función de cualquier otro estado o accesorios en su componente? Si es así, no es estado.
Actualización 2016: Reacción cambia, y la explicación "props vs state" se volvió muy simple. Si un componente necesita cambiar datos, colóquelo en un estado, de lo contrario, en accesorios. Porque los accesorios son de solo lectura ahora.
¿Cuál es la diferencia exacta entre los accesorios y el estado?
Puedes encontrar una buena explicación here (versión completa)