will update trigger react prevstate mounted immediately here did componentdidupdate componentdidmount component called after javascript reactjs

javascript - update - this.setState no está fusionando estados como esperaría



setstate reactjs (11)

Tengo el siguiente estado:

this.setState({ selected: { id: 1, name: ''Foobar'' } });

Luego actualizo el estado:

this.setState({ selected: { name: ''Barfoo'' }});

Como se supone que setState se fusionará, esperaría que fuera:

{ selected: { id: 1, name: ''Barfoo'' } };

Pero en cambio se come el id y el estado es:

{ selected: { name: ''Barfoo'' } };

¿Es este comportamiento esperado y cuál es la solución para actualizar solo una propiedad de un objeto de estado anidado?


¿has establecido el estado inicial?

Usaré un poco de mi propio código, por ejemplo:

getInitialState: function () { return { dragPosition: { top : 0, left : 0 }, editValue : "", dragging : false, editing : false }; }

En una aplicación en la que estoy trabajando, así es como he estado configurando y usando el estado. Creo que en setState puedes editar los estados que quieras individualmente. Lo he estado llamando así:

onChange: function (event) { event.preventDefault(); event.stopPropagation(); this.setState({editValue: event.target.value}); },

Tenga en cuenta que debe establecer el estado dentro de la función React.createClass que llamó getInitialState


A partir de ahora,

Si el siguiente estado depende del estado anterior, recomendamos utilizar el formulario de función de actualización, en su lugar:

de acuerdo con la documentación https://reactjs.org/docs/react-component.html#setstate , usando:

this.setState((prevState) => { return {quantity: prevState.quantity + 1}; });


Creo que setState() no hace una fusión recursiva.

Puede usar el valor del estado actual this.state.selected para construir un nuevo estado y luego llamar a setState() sobre eso:

var newSelected = _.extend({}, this.state.selected); newSelected.name = ''Barfoo''; this.setState({ selected: newSelected });

He utilizado la función función _.extend() (desde la biblioteca underscore.js) aquí para evitar la modificación de la parte selected del estado al crear una copia superficial de la misma.

Otra solución sería escribir setStateRecursively() que fusiona recursivamente en un nuevo estado y luego llama a replaceState() con él:

setStateRecursively: function(stateUpdate, callback) { var newState = mergeStateRecursively(this.state, stateUpdate); this.replaceState(newState, callback); }


Dado que muchas de las respuestas usan el estado actual como base para fusionarse en nuevos datos, quería señalar que esto puede romperse. Los cambios de estado están en cola y no modifican inmediatamente el objeto de estado de un componente. Hacer referencia a los datos de estado antes de que se haya procesado la cola, por lo tanto, le dará datos obsoletos que no reflejan los cambios pendientes que realizó en setState. De los documentos:

setState () no cambia inmediatamente this.state sino que crea una transición de estado pendiente. El acceso a this.state después de llamar a este método puede devolver el valor existente.

Esto significa que el uso del estado "actual" como referencia en llamadas posteriores a setState no es confiable. Por ejemplo:

  1. Primera llamada a setState, poniendo en cola un cambio al objeto de estado
  2. Segunda llamada a setState. Su estado usa objetos anidados, por lo que desea realizar una combinación. Antes de llamar a setState, obtienes el objeto de estado actual. Este objeto no refleja los cambios en cola realizados en la primera llamada a setState, arriba, porque sigue siendo el estado original, que ahora debería considerarse "obsoleto".
  3. Realizar fusión. El resultado es el estado "obsoleto" original más los datos nuevos que acaba de configurar, los cambios desde la llamada a setState inicial no se reflejan. Tu llamada a setState pone en cola este segundo cambio.
  4. Reaccionar cola de procesos. Se procesa la primera llamada setState, actualizando el estado. Se procesa la segunda llamada a setState, actualizando el estado. El objeto del segundo setState ha reemplazado al primero, y como los datos que tenía al hacer esa llamada estaban obsoletos, los datos obsoletos modificados de esta segunda llamada han destruido los cambios realizados en la primera llamada, que se perdieron.
  5. Cuando la cola está vacía, React determina si se procesará, etc. En este punto representará los cambios realizados en la segunda llamada a setState, y será como si la primera llamada de setState nunca ocurriera.

Si necesita usar el estado actual (por ejemplo, para fusionar datos en un objeto anidado), setState acepta alternativamente una función como argumento en lugar de un objeto; la función se invoca después de las actualizaciones previas al estado y pasa el estado como argumento, por lo que se puede usar para garantizar que los cambios atómicos respeten los cambios anteriores.


El estado Reaccionar no realiza la fusión recursiva en setState mientras que espera que no haya actualizaciones de miembros de estado in situ al mismo tiempo. O bien debe copiar los arreglos / objetos cerrados usted mismo (con array.slice u Object.assign) o use la biblioteca dedicada.

Como éste. NestedLink admite directamente el manejo del estado compuesto React.

this.linkAt( ''selected'' ).at( ''name'' ).set( ''Barfoo'' );

Además, el enlace al nombre selected o selected.name se puede pasar a todas partes como un único elemento y modificado allí con el set .


Estoy usando clases es6, y terminé con varios objetos complejos en mi estado superior y estaba tratando de hacer que mi componente principal fuera más modular, así que creé un contenedor de clase simple para mantener el estado en el componente superior pero permitiendo más lógica local .

La clase contenedora toma una función como su constructor que establece una propiedad en el estado del componente principal.

export default class StateWrapper { constructor(setState, initialProps = []) { this.setState = props => { this.state = {...this.state, ...props} setState(this.state) } this.props = initialProps } render() { return(<div>render() not defined</div>) } component = props => { this.props = {...this.props, ...props} return this.render() } }

Luego, para cada propiedad compleja en el estado superior, creo una clase StateWrapped. Puede establecer los puntales predeterminados en el constructor aquí y se establecerán cuando se inicialice la clase, puede hacer referencia al estado local de los valores y establecer el estado local, referirse a las funciones locales y hacer que pase por alto la cadena:

class WrappedFoo extends StateWrapper { constructor(...props) { super(...props) this.state = {foo: "bar"} } render = () => <div onClick={this.props.onClick||this.onClick}>{this.state.foo}</div> onClick = () => this.setState({foo: "baz"}) }

Entonces, mi componente de nivel superior solo necesita que el constructor configure cada clase en su propiedad de estado de nivel superior, una representación simple y cualquier función que comunique componentes cruzados.

class TopComponent extends React.Component { constructor(...props) { super(...props) this.foo = new WrappedFoo( props => this.setState({ fooProps: props }) ) this.foo2 = new WrappedFoo( props => this.setState({ foo2Props: props }) ) this.state = { fooProps: this.foo.state, foo2Props: this.foo.state, } } render() { return( <div> <this.foo.component onClick={this.onClickFoo} /> <this.foo2.component /> </div> ) } onClickFoo = () => this.foo2.setState({foo: "foo changed foo2!"}) }

Parece funcionar bastante bien para mis propósitos, tenga en cuenta que no puede cambiar el estado de las propiedades que asigna a los componentes envueltos en el componente de nivel superior ya que cada componente envuelto está rastreando su propio estado pero actualizando el estado en el componente superior cada vez que cambia


Los ayudantes de inmutabilidad se agregaron recientemente a React.addons, así que con eso, ahora puedes hacer algo como:

var newState = React.addons.update(this.state, { selected: { name: { $set: ''Barfoo'' } } }); this.setState(newState);

Documentación de ayudantes de inmutabilidad: http://facebook.github.io/react/docs/update.html


Mi solución para este tipo de situación es usar, como otra respuesta señalada, http://facebook.github.io/react/docs/update.html .

Como establecer el estado en profundidad es una situación común, he creado el siguiente mixin:

var SeStateInDepthMixin = { setStateInDepth: function(updatePath) { this.setState(React.addons.update(this.state, updatePath);); } };

Este mixin está incluido en la mayoría de mis componentes y generalmente ya no uso setState directamente.

Con este mixin, todo lo que necesita hacer para lograr el efecto deseado es llamar a la función setStateinDepth de la siguiente manera:

setStateInDepth({ selected: { name: { $set: ''Barfoo'' }}})

Para más información:


No quería instalar otra biblioteca así que aquí hay otra solución.

En lugar de:

this.setState({ selected: { name: ''Barfoo'' }});

Haz esto en su lugar:

var newSelected = Object.assign({}, this.state.selected); newSelected.name = ''Barfoo''; this.setState({ selected: newSelected });

O, gracias a @ icc97 en los comentarios, incluso de manera más sucinta, pero posiblemente menos legible:

this.setState({ selected: Object.assign({}, this.state.selected, { name: "Barfoo" }) });

Además, para ser claros, esta respuesta no viola ninguna de las preocupaciones que @bgannonpl mencionó anteriormente.


Preservando el estado anterior basado en @bgannonpl respuesta:

Ejemplo de Lodash :

this.setState((previousState) => _.merge({}, previousState, { selected: { name: "Barfood"} }));

Para comprobar que funcionó correctamente, puede usar la devolución de llamada de la segunda función de parámetro:

this.setState((previousState) => _.merge({}, previousState, { selected: { name: "Barfood"} }), () => alert(this.state.selected));

Usé merge porque extend descarta las otras propiedades de lo contrario.

Ejemplo de Inmutabilidad de Reacción :

import update from "react-addons-update"; this.setState((previousState) => update(previousState, { selected: { name: {$set: "Barfood"} } });


Yo uso el tmp var para cambiar.

changeTheme(v) { let tmp = this.state.tableData tmp.theme = v this.setState({ tableData : tmp }) }