You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React聊天消息默认滚动到底部无滚动动画问题求助

解决聊天页面默认滚动到底部的可见动画问题

核心问题分析

当前实现存在两个关键问题导致滚动动画可见:

  • 使用behavior: 'smooth'触发平滑滚动,产生了可见的滚动过程
  • loading状态切换时机与DOM渲染顺序不匹配,导致消息先显示在顶部再触发滚动

具体解决方案

1. 改为瞬间滚动,移除平滑动画

修改scrollToBottom函数,将滚动行为设置为auto(默认值,瞬间完成滚动,无动画):

const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'auto' });
};

2. 优化加载状态的视觉隐藏逻辑

通过CSS在加载阶段隐藏消息容器,避免消息顶部提前暴露:

  • 给容器添加加载状态类:
<div className={`chat-messages ${loading ? 'chat-messages--loading' : ''}`}>
  • 在chat-messages.scss中补充对应样式:
.chat-messages--loading {
    visibility: hidden;
    overflow: hidden;
    height: 0;
}

3. 避免不必要的useLayoutEffect重复触发

当前messages变量每次渲染都会重新创建,导致useLayoutEffect反复执行。用useMemo缓存消息数组:

import { useRef, useLayoutEffect, useState, useContext, useMemo } from 'react';

// ...

const messages = useMemo(() => Array.from({ length: 10 }).reverse(), []);

4. 调整状态更新时机,确保滚动完成后再显示内容

用requestAnimationFrame保证滚动完成后再关闭加载状态,让用户直接看到底部的消息:

useLayoutEffect(() => {
    scrollToBottom();
    requestAnimationFrame(() => {
        setLoadingMessages(false);
    });
}, [messages]);

最终修改后的完整代码

'use client';

import '@styles/chat-messages.scss';
import ChatMessage from '@components/chat-message';
import Spinner from '@components/spinner';
import { LoadingContext } from '@components/providers/LoadingProvider';

import { useRef, useLayoutEffect, useState, useContext, useMemo } from 'react';

export default function ChatMessages() {
    const [loading, setLoadingMessages] = useState(true);
    // const {setLoading} = useContext(LoadingContext);
    const messages = useMemo(() => Array.from({ length: 10 }).reverse(), []);

    const messagesEndRef = useRef<HTMLDivElement>(null);

    const scrollToBottom = () => {
        messagesEndRef.current?.scrollIntoView({ behavior: 'auto' });
    };

    useLayoutEffect(() => {
        scrollToBottom();
        requestAnimationFrame(() => {
            setLoadingMessages(false);
        });
    }, [messages]);

    return (
        <div className={`chat-messages ${loading ? 'chat-messages--loading' : ''}`}>
            {    
                !loading &&
                (                
                    messages.map((_, index) => (
                        <ChatMessage key={index} i={index} />
                ))
                )
            }
            {
                loading && (
                    <Spinner />
                )
            }
            <div ref={messagesEndRef} className='chat-messages__point'></div>
        </div>
    );
}

内容的提问来源于stack exchange,提问作者Eugene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:02:26