¿Por qué es setState en reactjs Async en lugar de Sync?

126

Acabo de descubrir que la this.setState()función de reacción en cualquier componente es asíncrona o se llama después de completar la función en la que se llamó.

Ahora busqué y encontré este blog ( la operación de mutación de estado setState () puede ser sincrónica en ReactJS )

Aquí descubrió que setStatees asíncrono (llamado cuando la pila está vacía) o sincronización (llamado tan pronto como se llama) dependiendo de cómo se activó el cambio de estado.

Ahora estas dos cosas son difíciles de digerir

  1. En el blog, la setStatefunción se llama dentro de una función updateState, pero lo que la activó updateStateno es algo que una función llamada sabría.
  2. ¿Por qué harían setStateasíncrono ya que JS es un lenguaje de subproceso único y este setState no es una WebAPI o una llamada al servidor, por lo que debe hacerse solo en el subproceso de JS? ¿Están haciendo esto para que Re-Rendering no detenga a todos los oyentes de eventos y otras cosas, o hay algún otro problema de diseño?
Anup
fuente
1
Hoy escribí un artículo que ayuda a describir un poco del clima setState: medium.com/@agm1984/…
agm1984

Respuestas:

155

Puede llamar a una función después de que se haya actualizado el valor del estado:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

Además, si tiene muchos estados para actualizar a la vez, agrúpelos todos de la misma manera setState:

En vez de:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Solo haz esto:

this.setState({
    foo: "one",
    bar: "two"
});
JoeTidee
fuente
16
ya está bien, tenemos una función callBack que podemos usar pero no es la pregunta.
Anup
12
Esperemos que ayude a alguien más a tropezar con esta pregunta.
JoeTidee
2
eso puede ser útil
Anup
97

1) las setStateacciones son asíncronas y se agrupan para obtener ganancias de rendimiento. Esto se explica en la documentación de setState.

setState () no muta inmediatamente this.state pero crea una transición de estado pendiente. Acceder a this.state después de llamar a este método puede potencialmente devolver el valor existente. No hay garantía de funcionamiento sincrónico de las llamadas a setState y las llamadas se pueden agrupar para aumentar el rendimiento.


2) ¿Por qué harían setState async ya que JS es un lenguaje de subproceso único y esto setStateno es una WebAPI o una llamada al servidor?

Esto se debe a que setStatealtera el estado y provoca el reenvío. Esta puede ser una operación costosa y hacerla sincrónica podría dejar al navegador sin respuesta.

Por lo tanto, las llamadas setState son asíncronas y están agrupadas para una mejor experiencia y rendimiento de la interfaz de usuario.

Sachin
fuente
59
Si necesita asegurarse de ordenar los eventos después de realizar una llamada setState, puede pasar una función de devolución de llamada. this.setState({ something: true }, () => console.log(this.state))
ianks
1
Gracias @Sachin por la explicación. Sin embargo, todavía tengo dudas, ¿puede ser sincrónico como explica el blog?
Ajay Gaur
2
Otra estúpida decisión de diseño en reaccionar. Haga que la actualización de estado sea síncrona y que la representación sea asíncrona. Puede renderizar por lotes, pero quiero poder establecer algo tan primitivo como las variables de estado sin tener que lidiar con las condiciones de carrera.
ig-dev
¿Por qué no permitir que se configure una opción para que la función sea asíncrona o sincronizada? Esa sería una característica útil
Randall Coding el
Obviamente, no estoy en el equipo de reacción, pero una razón, en mi opinión, para hacer que la actualización de estado sea asíncrona es que los navegadores son de un solo subproceso. Las operaciones de sincronización pueden hacer que la interfaz de usuario no responda y no son un buen candidato para la interfaz de usuario.
Sachin
16

Sé que esta pregunta es antigua, pero ha causado mucha confusión a muchos usuarios de reactjs durante mucho tiempo, incluyéndome a mí. Recientemente, Dan Abramov (del equipo de reacción) acaba de escribir una gran explicación de por qué la naturaleza de esto setStatees asíncrona:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setStateestá destinado a ser asíncrono, y hay algunas razones realmente buenas para eso en la explicación vinculada de Dan Abramov. Esto no significa que siempre será asíncrono, principalmente significa que simplemente no puede depender de que sea síncrono . ReactJS toma en consideración muchas variables en el escenario en el que está cambiando el estado, para decidir cuándo statedebería actualizarse realmente y volver a procesar su componente.
Un ejemplo simple para demostrar esto es que si llama setStatecomo reacción a una acción del usuario, entonces stateprobablemente se actualizará de inmediato (aunque, de nuevo, no puede contar con eso), por lo que el usuario no sentirá ningún retraso , pero si llamassetState en respuesta a una respuesta de llamada ajax o algún otro evento que no sea activado por el usuario, entonces el estado podría actualizarse con un ligero retraso, ya que el usuario realmente no sentirá este retraso y mejorará el rendimiento al esperar agrupe varias actualizaciones de estado juntas y vuelva a procesar el DOM menos veces.

gillyb
fuente
No has marcado ningún contestador como el correcto. La gente está publicando cómo evitarlo. No es la respuesta a la pregunta formulada. Este artículo parece bueno.
Anup
@Anup La respuesta es un poco más complicada que solo 'async' o 'sync'. Siempre debe tratarse como 'asíncrono', pero en algunos casos podría actuar 'sincronizado'. Espero arrojar algo de luz para ti.
gillyb
8

Buen artículo aquí https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

o pasar devolución de llamada this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

zloctb
fuente
7

Puede usar el siguiente ajuste para realizar llamadas de sincronización

this.setState((state =>{
  return{
    something
  }
})

Ярослав
fuente
respuesta subestimada
James Cat
1

Imagina incrementar un contador en algún componente:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

Hay un controlador de recuento adjunto a los componentes primarios y secundarios. Esto se hace a propósito para que podamos ejecutar el setState () dos veces dentro del mismo contexto de evento de clic, pero desde 2 controladores diferentes.

Como podríamos imaginar, un evento de un solo clic en el botón ahora activaría estos dos controladores ya que el evento burbujea desde el objetivo al contenedor más externo durante la fase de burbujeo.

Por lo tanto, btnCountHandler () se ejecuta primero, se espera que incremente la cuenta a 1 y luego se ejecuta divCountHandler (), que se espera que incremente la cuenta a 2.

Sin embargo, el recuento solo aumenta a 1, como puede inspeccionar en las herramientas React Developer.

Esto prueba que reaccionan

  • pone en cola todas las llamadas setState

  • vuelve a esta cola después de ejecutar el último método en el contexto (el divCountHandler en este caso)

  • combina todas las mutaciones de objetos que ocurren dentro de múltiples llamadas setState en el mismo contexto (todas las llamadas a métodos dentro de una sola fase de evento es el mismo contexto, por ejemplo) en una sintaxis de mutación de un solo objeto (la fusión tiene sentido porque es por eso que podemos actualizar las propiedades de estado de forma independiente en setState () en primer lugar)

  • y lo pasa a un solo setState () para evitar que se vuelva a generar debido a múltiples llamadas setState () (esta es una descripción muy primitiva del procesamiento por lotes).

Código resultante ejecutado por react:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

Para detener este comportamiento, en lugar de pasar objetos como argumentos al método setState, se pasan devoluciones de llamada.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

Después de que el último método finaliza la ejecución y cuando reacciona vuelve a procesar la cola setState, simplemente llama a la devolución de llamada para cada setState en cola, pasando el estado del componente anterior.

Esta forma de reaccionar asegura que la última devolución de llamada en la cola actualice el estado en el que todas sus contrapartes anteriores se han puesto manos a la obra.

supi
fuente
0

Sí, setState () es asíncrono.

Desde el enlace: https://reactjs.org/docs/react-component.html#setstate

  • React no garantiza que los cambios de estado se apliquen de inmediato.
  • setState () no siempre actualiza inmediatamente el componente.
  • Piense en setState () como una solicitud en lugar de un comando inmediato para actualizar el componente.

Porque piensan
Desde el enlace: https://github.com/facebook/react/issues/11527#issuecomment-360199710

... estamos de acuerdo en que la representación de setState () sincrónicamente sería ineficiente en muchos casos

AsSinchronous setState () hace que la vida sea muy difícil para aquellos que comienzan e incluso experimentan desafortunadamente:
- problemas de renderizado inesperados: renderizado retrasado o sin renderizado (basado en la lógica del programa)
- pasar parámetros es un gran problema
entre otros problemas.

El siguiente ejemplo ayudó:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Espero que ayude.

Manohar Reddy Poreddy
fuente