Solid-js中更新消息后如何等待Div重绘再执行滚动?
SolidJS 更新消息后滚动到底部的最优方案
问题描述
我编写了一段SolidJS代码,需求是监听topic变化时,获取新消息后让容器重绘最新内容,再滚动到底部。但setMessages(chat)并未立即触发DOM重绘,导致滚动代码执行时机过早,滚动位置不准确。尝试用createRenderEffect实现更新消息和滚动,但消息无法正确更新。目前有两个临时方案:
- 将滚动代码放入0ms的
setTimeout中 - 用async函数包装消息更新并await后执行滚动
想请教是否有更优的解决方案?
代码示例
import { For, createEffect, on, type VoidComponent } from "solid-js"; import { createSignal } from "solid-js"; import { createStore } from "solid-js/store"; function getRandomInt(min: number, max: number) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } function getTopicChat() { const n = getRandomInt(12, 24); return Array.from(Array(n).keys()); } const Home: VoidComponent = () => { let containerDiv!: HTMLDivElement; const [topic, setTopic] = createSignal("TOPIC 1"); const [messages, setMessages] = createStore<number[]>([]); createEffect( on(topic, async () => { const chat = getTopicChat(); setMessages(chat); containerDiv.scroll({ top: containerDiv.scrollHeight }); }) ); return ( <main class="h-screen"> <div class="flex justify-center"> <button onClick={() => setTopic("Topic " + getRandomInt(2, 99).toString())} class="bg-red-100 py-2 px-4 rounded-md my-2" > Change topic </button> </div> <div class="text-center"> <p>{topic()}</p> </div> <div class="mx-20 overflow-y-auto h-96" ref={containerDiv}> <For each={messages}> {(i) => <div class="bg-green-200 h-40 my-4">{i}</div>} </For> </div> </main> ); }; export default Home;
尝试过的createRenderEffect代码
createRenderEffect( on(topic, () => { const chat = getTopicChat(); setMessages(chat); if (containerDiv) { containerDiv.scroll({ top: containerDiv.scrollHeight }); } }) );
最优解决方案
核心是利用SolidJS的响应式机制,在DOM更新完成后再执行滚动操作。可以通过单独监听messages的变化来实现,因为createEffect会在DOM渲染完成后触发:
方案代码
import { For, createEffect, on, type VoidComponent } from "solid-js"; import { createSignal } from "solid-js"; import { createStore } from "solid-js/store"; function getRandomInt(min: number, max: number) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } function getTopicChat() { const n = getRandomInt(12, 24); return Array.from(Array(n).keys()); } const Home: VoidComponent = () => { let containerDiv!: HTMLDivElement; const [topic, setTopic] = createSignal("TOPIC 1"); const [messages, setMessages] = createStore<number[]>([]); // 监听topic变化,仅负责更新消息 createEffect( on(topic, () => { const chat = getTopicChat(); setMessages(chat); }) ); // 监听messages变化,DOM更新后执行滚动 createEffect(() => { // 读取messages建立依赖,确保DOM已完成更新 messages; containerDiv.scroll({ top: containerDiv.scrollHeight }); }); return ( <main class="h-screen"> <div class="flex justify-center"> <button onClick={() => setTopic("Topic " + getRandomInt(2, 99).toString())} class="bg-red-100 py-2 px-4 rounded-md my-2" > Change topic </button> </div> <div class="text-center"> <p>{topic()}</p> </div> <div class="mx-20 overflow-y-auto h-96" ref={containerDiv}> <For each={messages}> {(i) => <div class="bg-green-200 h-40 my-4">{i}</div>} </For> </div> </main> ); }; export default Home;
方案说明
- 拆分逻辑:将消息更新和滚动操作分离,
topic变化时仅处理消息更新逻辑 - 依赖
messages的createEffect会在DOM渲染完成后触发,此时容器的scrollHeight已经对应新消息的高度,滚动位置准确 - 完全利用SolidJS的响应式系统,避免了定时器或异步包装带来的不确定性,代码更贴合框架设计规范
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

