在react项目中,实现添加列表项,最后一项自动显示在可视区域范围!!
实现效果
代码实现
import { useState, useRef } from "react";
import { flushSync } from "react-dom";
function FlushSyncRef() {
const [msgLists, setMsgList] = useState([]);
const [msg, setMsg] = useState("");
const ulRef = useRef(null);
const handleAdd = (e) => {
e.preventDefault();
setMsg("");
setMsgList((prev) => {
console.log(prev);
return [...prev, msg];
});
ulRef.current.lastChild.scrollIntoView({
behavior: "smooth",
block: "end",
});
console.log("🚀 ~ handleAdd ~ ulRef.current:", ulRef.current.lastChild);
};
function handleChangeText(e) {
setMsg(e.target.value);
}
return (
<div>
<input
type="text"
placeholder="Enter your msg"
value={msg}
onChange={handleChangeText}
/>
<button onClick={handleAdd}>添加</button>
<hr />
<ul
style={{ height: "90px", border: "1px solid red", overflow: "auto" }}
ref={ulRef}
>
{msgLists.map((item, index) => {
return (
<li key={index}>
这是第{index}
{item}
</li>
);
})}
</ul>
</div>
);
}
export default FlushSyncRef;
测试发现,组件崩溃了!!!!!
意思就是没有找到要滚动的元素,元素为null,上面不存在这个scroll
方法。
在默认数据改造下
可以发现,最后一项始终慢了一步,不能同步的显示到可视区域。
为啥为这样呢?
在 React 中,state 更新是排队进行的。通常,这就是你想要的。但是,在这个示例中会导致问题,因为 setTodos 不会立即更新 DOM。因此,当你将列表滚动到最后一个元素时,尚未添加待办事项。这就是为什么滚动总是“落后”一项的原因。
要解决此问题,你可以强制 React 同步更新(“刷新”)DOM
。 为此,从 react-dom
导入 flushSync
并将 state 更新包裹 到flushSync
中
- 修复后的代码
const [msgLists, setMsgList] = useState(["test0000"]);
// ....
const handleAdd = (e) => {
e.preventDefault();
flushSync(() => {
setMsg("");
setMsgList((prev) => {
console.log(prev);
return [...prev, msg];
});
});
ulRef.current?.lastChild?.scrollIntoView({
behavior: "smooth",
block: "end",
});
console.log("🚀 ~ handleAdd ~ ulRef.current:", ulRef.current.lastChild);
};
// ....
这样,再测试就实现了我们的需求了。