¿Funciona la actualización del estado por lotes de React cuando se utilizan ganchos?

Resuelto vadirn asked hace 6 años • 7 respuestas

Para componentes de clase, this.setStatellama por lotes si está dentro de los controladores de eventos. Pero, ¿qué sucede si el estado se actualiza fuera del controlador de eventos y se utiliza useStateun gancho?

function Component() {
  const [a, setA] = useState('a');
  const [b, setB] = useState('b');

  function handleClick() {
    Promise.resolve().then(() => {
      setA('aa');
      setB('bb');
    });
  }

  return <button onClick={handleClick}>{a}-{b}</button>
}

¿ Se renderizará aa - bbde inmediato? ¿O será aa - by entonces aa - bb?

vadirn avatar Oct 29 '18 22:10 vadirn
Aceptado

TL;DR : si los cambios de estado se activan de forma asincrónica (por ejemplo, envueltos en una promesa), no se agruparán; si se activan directamente, se agruparán.

Configuré una zona de pruebas para probar esto: https://codesandbox.io/s/402pn5l989

import React, { Fragment, useState } from 'react';
import ReactDOM from 'react-dom';

import './styles.css';

function Component() {
  const [a, setA] = useState('a');
  const [b, setB] = useState('b');
  console.log('a', a);
  console.log('b', b);

  function handleClickWithPromise() {
    Promise.resolve().then(() => {
      setA('aa');
      setB('bb');
    });
  }

  function handleClickWithoutPromise() {
    setA('aa');
    setB('bb');
  }

  return (
    <Fragment>
    <button onClick={handleClickWithPromise}>
      {a}-{b} with promise
    </button>
    <button onClick={handleClickWithoutPromise}>
      {a}-{b} without promise
    </button>
      </Fragment>
  );
}

function App() {
  return <Component />;
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

Creé dos botones, uno activa los cambios de estado envueltos en una promesa como en su ejemplo de código, el otro activa los cambios de estado directamente.

Si miras la consola, cuando presiones el botón “con promesa”, primero mostrará a aay b b, luego a aay b bb.

Entonces la respuesta es no, en este caso, no se renderizará aa - bbde inmediato, cada cambio de estado desencadena una nueva renderización, no hay procesamiento por lotes.

Sin embargo, cuando haces clic en el botón "sin promesa", la consola se mostrará a aae b bbinmediatamente.

Entonces, en este caso, React procesa por lotes los cambios de estado y procesa ambos juntos.

Patrick Hund avatar Oct 29 '2018 15:10 Patrick Hund

Actualmente, en React v16 y versiones anteriores, solo las actualizaciones dentro de los controladores de eventos de React, como clicketc. onChange, se agrupan de forma predeterminada. Entonces, al igual que las actualizaciones de estado de las clases, se agrupan de manera similar en los ganchos.

Existe una API inestable para forzar el procesamiento por lotes fuera de los controladores de eventos en casos excepcionales cuando lo necesite.

ReactDOM.unstable_batchedUpdates(() => { ... })

Existe un plan para agrupar todas las actualizaciones de estado en versiones futuras en React, probablemente v17 o superior.

Ahora también, si las llamadas de actualización de estado desde dentro del controlador de eventos están en funciones asíncronas o se activan debido a un código asíncrono, no se agruparán en lotes donde se agruparán las actualizaciones directas.

Donde sin el código de sincronización las actualizaciones de estado se realizan por lotes y las actualizaciones del código asíncrono no

function App() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  // async update from useEffect
  useEffect(() => {
    setTimeout(() => {
      setCount1(count => count + 1);
      setCount2(count => count + 2);
    }, 3000);
  }, []);

  const handleAsyncUpdate = async () => {
    await Promise.resolve("state updated");
    setCount1(count => count + 2);
    setCount2(count => count + 1);
  };

  const handleSyncUpdate = () => {
    setCount1(count => count + 2);
    setCount2(count => count + 1);
  };

  console.log("render", count1, count2);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button type="button" onClick={handleAsyncUpdate}>
        Click for async update
      </button>
      <button type="button" onClick={handleSyncUpdate}>
        Click for sync update
      </button>
    </div>
  );
}

https://codesandbox.io/s/739rqyyqmq

Shubham Khatri avatar Apr 04 '2019 18:04 Shubham Khatri