Работает ли функция обновления состояния пакета React при использовании хуков?
Для компонентов класса this.setState
вызывает пакет, если внутри обработчиков событий. Но что произойдет, если состояние будет обновлено вне обработчика событий и с использованием хука useState
?
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>
}
Будет ли это сразу aa - bb
? Или это будет aa - b
а затем aa - bb
?
Ответы
Ответ 1
TL; DR - если изменения состояния инициируются асинхронно (например, заключены в обещание), они не будут упакованы; если они запускаются напрямую, они будут группироваться.
Я установил песочницу, чтобы попробовать это: 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);
Я сделал две кнопки, одна из которых вызывает изменения состояния, обернутые в обещание, как в вашем примере кода, а другая - непосредственно.
Если вы посмотрите на консоль, когда вы нажмете кнопку "с обещанием", она сначала покажет a aa
и bb
, затем a aa
и b bb
.
Таким образом, ответ - нет, в этом случае он не будет визуализировать aa - bb
сразу, каждое изменение состояния запускает новый рендеринг, пакетирование не выполняется.
Однако, когда вы нажмете кнопку "без обещания", консоль сразу покажет a aa
и b bb
.
Таким образом, в этом случае React пакетно изменяет состояние и делает один для обоих вместе.
Ответ 2
В настоящее время в React v16 и более ранних onChange
по умолчанию упаковываются только обновления внутри обработчиков событий React, таких как click
или onChange
т.д. Так же, как обновления состояний классов упакованы аналогичным образом в хуках
Существует нестабильный API для принудительной пакетной обработки вне обработчиков событий в редких случаях, когда это необходимо.
ReactDOM.unstable_batchedUpdates(() => { ... })
Планируется пакетное обновление всех обновлений состояния в будущих версиях на вероятно, v17 или выше.
Теперь также, если вызовы обновления состояния из обработчика событий находятся в асинхронных функциях или вызваны из-за асинхронного кода, они не будут пакетироваться, когда прямые обновления будут пакетироваться
Где без синхронизации состояния кода обновления пакетируются, а обновления асинхронного кода не
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
Ответ 3
Если обработчиком события является react-based
, он пакетирует обновления. Это верно как для вызовов setState, так и для useState.
Но он не пакетируется автоматически, если событие основано на non-react
, то есть setTimeout, Promise вызывает. Короче говоря, любое событие из веб-API.