reactjs - tabla - Reaccionar: ¿la forma correcta de pasar el estado del elemento de forma a los elementos hermanos/padres?
tabla periodica (5)
- Supongamos que tengo una clase React P, que representa dos clases secundarias, C1 y C2.
- C1 contiene un campo de entrada. Me referiré a este campo de entrada como Foo.
- Mi objetivo es dejar que C2 reaccione a los cambios en Foo.
He encontrado dos soluciones, pero ninguna de ellas se siente bien.
Primera solución:
- Asignar P a estado,
state.input
. - Cree una función
onChange
en P, que toma un evento y establecestate.input
. - Pase esto en
onChange
a C1 comoonChange
, y deje que C1 vinculethis.props.onChange
alonChange
de Foo.
Esto funciona. Cada vez que el valor de Foo cambia, desencadena un setState
en P, por lo que P tendrá la entrada para pasar a C2.
Pero no se siente del todo bien por la misma razón: estoy configurando el estado de un elemento padre de un elemento hijo. Esto parece traicionar el principio de diseño de React: flujo de datos en una sola dirección.
¿Es así como se supone que debo hacerlo, o hay una solución más React-natural?
Segunda solución:
Solo ponle Foo en P.
¿Pero este es un principio de diseño que debería seguir cuando estructurar mi aplicación, poniendo todos los elementos de la forma en el render
de la clase de más alto nivel?
Como en mi ejemplo, si tengo una representación grande de C1, realmente no quiero poner la render
completa de C1 para render
P porque C1 tiene un elemento de forma.
¿Cómo debería hacerlo?
- Lo correcto es tener el estado en el componente padre , para evitar la referencia y lo que no
- Un problema es evitar actualizar constantemente a todos los niños al escribir en un campo
- Por lo tanto, cada hijo debe ser un Componente (como no es un PureComponent) e implementar
shouldComponentUpdate(nextProps, nextState)
- De esta forma, al escribir en un campo de formulario, solo ese campo se actualiza
El siguiente código usa anotaciones @bound
de ES.Next babel-plugin-transform-decorators-legacy
de BabelJS 6 y class-properties (la anotación establece este valor en las funciones de miembros similares a bind):
/*
© 2017-present Harald Rudell <[email protected]> (http://www.haraldrudell.com)
All rights reserved.
*/
import React, {Component} from ''react''
import {bound} from ''class-bind''
const m = ''Form''
export default class Parent extends Component {
state = {one: ''One'', two: ''Two''}
@bound submit(e) {
e.preventDefault()
const values = {...this.state}
console.log(`${m}.submit:`, values)
}
@bound fieldUpdate({name, value}) {
this.setState({[name]: value})
}
render() {
console.log(`${m}.render`)
const {state, fieldUpdate, submit} = this
const p = {fieldUpdate}
return (
<form onSubmit={submit}> {/* loop removed for clarity */}
<Child name=''one'' value={state.one} {...p} />
<Child name=''two'' value={state.two} {...p} />
<input type="submit" />
</form>
)
}
}
class Child extends Component {
value = this.props.value
@bound update(e) {
const {value} = e.target
const {name, fieldUpdate} = this.props
fieldUpdate({name, value})
}
shouldComponentUpdate(nextProps) {
const {value} = nextProps
const doRender = value !== this.value
if (doRender) this.value = value
return doRender
}
render() {
console.log(`Child${this.props.name}.render`)
const {value} = this.props
const p = {value}
return <input {...p} onChange={this.update} />
}
}
Debe aprender la biblioteca Redux y ReactRedux. Estructurará sus estados y accesorios en una tienda y podrá acceder a ellos más adelante en sus componentes.
Después de haber usado Reaccionar para construir una aplicación ahora, me gustaría compartir algunas reflexiones sobre esta pregunta que hice hace medio año.
Te recomiendo que leas
La primera publicación es extremadamente útil para comprender cómo debe estructurar su aplicación React.
Flux responde la pregunta de por qué debería estructurar su aplicación React de esta manera (en lugar de cómo estructurarla). Reaccionar es solo el 50% del sistema, y con Flux puedes ver la imagen completa y ver cómo constituyen un sistema coherente.
De vuelta a la pregunta.
En cuanto a mi primera solución, está totalmente bien permitir que el manejador vaya en la dirección opuesta, ya que los datos todavía están en una sola dirección.
Sin embargo, si dejar que un controlador active un setState en P puede ser correcto o incorrecto dependiendo de su situación.
Si la aplicación es un simple convertidor de Markdown, C1 es la entrada en bruto y C2 es la salida de HTML, está bien permitir que C1 active un setState en P, pero algunos podrían argumentar que esta no es la manera recomendada de hacerlo.
Sin embargo, si la aplicación es una lista de cosas pendientes, C1 es la entrada para crear un nuevo todo, C2 la lista de tareas pendientes en HTML, es probable que desee manipular para ir dos niveles más arriba que P - al dispatcher
, que permite que la store
actualice el data store
, que luego envía los datos a P y llena las vistas. Ver ese artículo de Flux. Aquí hay un ejemplo: Flux - TodoMVC
En general, prefiero la manera descrita en el ejemplo de la lista de tareas pendientes. Cuanto menos estado tengas en tu aplicación, mejor.
Entonces, si lo entiendo correctamente, ¿su primera solución sugiere que mantiene el estado en su componente raíz? No puedo hablar por los creadores de React, pero en general, considero que esta es una solución adecuada.
Mantener el estado es una de las razones (al menos creo) de que React fue creado. Si alguna vez ha implementado su propio cliente de modelo de estado para lidiar con una interfaz de usuario dinámica que tiene muchas piezas móviles interdependientes, entonces le encantará Reaccionar, ya que alivia mucho este dolor de gestión estatal.
Al mantener el estado más arriba en la jerarquía y actualizarlo a través de eventos, el flujo de datos sigue siendo bastante unidireccional, solo está respondiendo a los eventos en el componente raíz, no está realmente obteniendo los datos allí a través del enlace bidireccional. le está diciendo al componente Root que "hey, algo sucedió aquí, revise los valores" o está pasando el estado de algunos datos en el componente secundario para actualizar el estado. Cambió el estado en C1 y desea que C2 lo tenga en cuenta, por lo tanto, actualizando el estado en el componente raíz y re-renderizando, los puntales de C2 ahora están sincronizados ya que el estado se actualizó en el componente raíz y se transfirió .
class Example extends React.Component {
constructor (props) {
super(props)
this.state = { data: ''test'' }
}
render () {
return (
<div>
<C1 onUpdate={this.onUpdate.bind(this)}/>
<C2 data={this.state.data}/>
</div>
)
}
onUpdate (data) { this.setState({ data }) }
}
class C1 extends React.Component {
render () {
return (
<div>
<input type=''text'' ref=''myInput''/>
<input type=''button'' onClick={this.update.bind(this)} value=''Update C2''/>
</div>
)
}
update () {
this.props.onUpdate(this.refs.myInput.getDOMNode().value)
}
})
class C2 extends React.Component {
render () {
return <div>{this.props.data}</div>
}
})
ReactDOM.renderComponent(<Example/>, document.body)
La primera solución, con mantener el estado en el componente principal , es la correcta . Sin embargo, para problemas más complejos, debe pensar en alguna biblioteca de gestión estatal , redux es la más popular utilizada con reaccionar.