一般情况下,State 更新是异步更新 dom 的,比如下边这个例子
import { useState, useRef } from 'react';
import { flushSync } from 'react-dom';
export default function TodoList() {
const [todos, setTodos] = useState(
Array(3).fill('').map((_, index) => ({
id: `${index + 1}`,
name: `todo_${index + 1}`
}))
);
function handleAdd() {
setTodos(todos => [ ...todos, {
id: `${todos.length + 1}`,
name: `todo_${todos.length + 1}`,
}]);
// 这里是拿不到 input 的,因为这时候 dom 还没更新上
const input = document.getElementById(`${todos.length + 1}`);
console.log(' --input--', input);
}
return (
<>
<button onClick={handleAdd}>
Add
</button>
<div>
{todos.map(item => {
const { name, id } = item;
return (
<div key={id}>
<input id={id} value={id} />
{name}
</div>
)
})}
</div>
</>
);
}
这时候可以使用 react-dom 提供的 flushSync,使得同步更新 dom:
import { useState, useRef } from 'react';
import { flushSync } from 'react-dom';
export default function TodoList() {
const [todos, setTodos] = useState(
Array(3).fill('').map((_, index) => ({
id: `${index + 1}`,
name: `todo_${index + 1}`
}))
);
function handleAdd() {
// 在这里调用,会同步更新 dom
flushSync(() => {
setTodos(todos => [ ...todos, {
id: `${todos.length + 1}`,
name: `todo_${todos.length + 1}`,
}]);
});
// 这时候这里拿到的 input 就是有的,因为 dom 已经同步渲染出来了
const input = document.getElementById(`${todos.length + 1}`)
console.log(' --input--', input.value)
}
return (
<>
<button onClick={handleAdd}>
Add
</button>
<div>
{todos.map(item => {
const { name, id } = item;
return (
<div key={id}>
<input id={id} value={id} />
{name}
</div>
)
})}
</div>
</>
);
}
但会有一个点:虽然调用 flushSync 同步更新 dom,但是组件的 state 更新仍然是异步的;
const [todos, setTodos] = useState(
Array(3).fill('').map((_, index) => ({
id: `${index + 1}`,
name: `todo_${index + 1}`
}))
);
function handleAdd() {
flushSync(() => {
setTodos(todos => [ ...todos, {
id: `${todos.length + 1}`,
name: `todo_${todos.length + 1}`,
}]);
});
const input = document.getElementById(`${todos.length + 1}`)
console.log(' --input--', input.value);
// 这里拿到的还是 旧值
console.log('todos: ', todos);
}