Que no haga scroll #193

Closed
opened 2026-03-18 22:56:40 +00:00 by AlexRG · 1 comment
Owner
  • Que cargue los último 10 mensajes
  • Que siempre te posicione en el más reciente
  • Que se implemente infinite scroll
- Que cargue los último 10 mensajes - Que siempre te posicione en el más reciente - Que se implemente _infinite scroll_
roberto.silva was assigned by AlexRG 2026-03-18 22:56:44 +00:00
AlexRG added the bug label 2026-03-18 22:56:52 +00:00
Author
Owner

👌
Aquí el cambio correcto es hacer que el chat salte directo al último mensaje cuando cambias de conversación o cuando cargan los mensajes, sin animación smooth y sin depender de scrollear manualmente.

Qué cambiar

Haz 3 ajustes:

1) agrega un ref para controlar la primera carga por chat

Pon esto junto a tus refs:

const hasAutoScrolledRef = useRef(false)

2) reemplaza tu scrollToBottom por una versión con opción instantánea

Cambia esta función:

const scrollToBottom = () => {
  if (scrollRef.current) {
    // Buscamos el viewport interno del ScrollArea de Radix
    const scrollContainer = scrollRef.current.querySelector(
      '[data-radix-scroll-area-viewport]',
    )
    if (scrollContainer) {
      scrollContainer.scrollTo({
        top: scrollContainer.scrollHeight,
        behavior: 'smooth',
      })
    }
  }
}

por esta:

const scrollToBottom = (behavior: ScrollBehavior = 'auto') => {
  if (!scrollRef.current) return

  const scrollContainer = scrollRef.current.querySelector(
    '[data-radix-scroll-area-viewport]',
  ) as HTMLDivElement | null

  if (!scrollContainer) return

  scrollContainer.scrollTo({
    top: scrollContainer.scrollHeight,
    behavior,
  })
}

3) reemplaza tu useEffect actual de scroll

Ahora tienes esto:

useEffect(() => {
  console.log(mensajesDelChat)

  scrollToBottom()
}, [chatMessages, isLoading])

Cámbialo por esto:

useEffect(() => {
  if (!activeChatId) return
  if (isLoadingMessages) return

  // primera carga del chat: ir directo al fondo sin animación
  if (!hasAutoScrolledRef.current) {
    requestAnimationFrame(() => {
      scrollToBottom('auto')
      hasAutoScrolledRef.current = true
    })
    return
  }

  // mensajes nuevos mientras ya estás dentro del chat
  requestAnimationFrame(() => {
    scrollToBottom('smooth')
  })
}, [activeChatId, chatMessages, isLoadingMessages])

4) resetea el autoscroll cuando cambies de chat o crees uno nuevo

Agrega este effect:

useEffect(() => {
  hasAutoScrolledRef.current = false
}, [activeChatId])

Y dentro de createNewChat, antes de setMessages(...), agrega:

hasAutoScrolledRef.current = false


Resultado final integrado

Te quedaría así en tu componente:

const scrollRef = useRef<HTMLDivElement>(null)
const hasAutoScrolledRef = useRef(false)

const scrollToBottom = (behavior: ScrollBehavior = 'auto') => {
  if (!scrollRef.current) return

  const scrollContainer = scrollRef.current.querySelector(
    '[data-radix-scroll-area-viewport]',
  ) as HTMLDivElement | null

  if (!scrollContainer) return

  scrollContainer.scrollTo({
    top: scrollContainer.scrollHeight,
    behavior,
  })
}

useEffect(() => {
  hasAutoScrolledRef.current = false
}, [activeChatId])

useEffect(() => {
  if (!activeChatId) return
  if (isLoadingMessages) return

  if (!hasAutoScrolledRef.current) {
    requestAnimationFrame(() => {
      scrollToBottom('auto')
      hasAutoScrolledRef.current = true
    })
    return
  }

  requestAnimationFrame(() => {
    scrollToBottom('smooth')
  })
}, [activeChatId, chatMessages, isLoadingMessages])

Y en createNewChat:

const createNewChat = () => {
  hasAutoScrolledRef.current = false
  setActiveChatId(undefined)
  setMessages([
    {
      id: 'welcome',
      role: 'assistant',
      content: 'Iniciando una nueva conversación. ¿En qué puedo ayudarte?',
    },
  ])
  setInput('')
}

Por qué así

  • auto en la primera carga = abre directo abajo
  • smooth después = cuando llegan mensajes nuevos, se siente natural
  • requestAnimationFrame ayuda porque ScrollArea de Radix a veces todavía no termina de pintar el viewport cuando corre el effect

Hay un detalle extra en tu código: estás usando RotateCcw pero no está importado 😅
Arriba te falta:

import { RotateCcw } from 'lucide-react'

Si quieres, te regreso el bloque completo ya pegado sobre tu archivo con el diff exacto.

Sí 👌 Aquí el cambio correcto es hacer que el chat **salte directo al último mensaje** cuando cambias de conversación o cuando cargan los mensajes, **sin animación smooth** y **sin depender de scrollear manualmente**. ### Qué cambiar Haz 3 ajustes: #### 1) agrega un ref para controlar la primera carga por chat Pon esto junto a tus refs: ```tsx const hasAutoScrolledRef = useRef(false) ``` --- #### 2) reemplaza tu `scrollToBottom` por una versión con opción instantánea Cambia esta función: ```tsx const scrollToBottom = () => { if (scrollRef.current) { // Buscamos el viewport interno del ScrollArea de Radix const scrollContainer = scrollRef.current.querySelector( '[data-radix-scroll-area-viewport]', ) if (scrollContainer) { scrollContainer.scrollTo({ top: scrollContainer.scrollHeight, behavior: 'smooth', }) } } } ``` por esta: ```tsx const scrollToBottom = (behavior: ScrollBehavior = 'auto') => { if (!scrollRef.current) return const scrollContainer = scrollRef.current.querySelector( '[data-radix-scroll-area-viewport]', ) as HTMLDivElement | null if (!scrollContainer) return scrollContainer.scrollTo({ top: scrollContainer.scrollHeight, behavior, }) } ``` --- #### 3) reemplaza tu `useEffect` actual de scroll Ahora tienes esto: ```tsx useEffect(() => { console.log(mensajesDelChat) scrollToBottom() }, [chatMessages, isLoading]) ``` Cámbialo por esto: ```tsx useEffect(() => { if (!activeChatId) return if (isLoadingMessages) return // primera carga del chat: ir directo al fondo sin animación if (!hasAutoScrolledRef.current) { requestAnimationFrame(() => { scrollToBottom('auto') hasAutoScrolledRef.current = true }) return } // mensajes nuevos mientras ya estás dentro del chat requestAnimationFrame(() => { scrollToBottom('smooth') }) }, [activeChatId, chatMessages, isLoadingMessages]) ``` --- #### 4) resetea el autoscroll cuando cambies de chat o crees uno nuevo Agrega este effect: ```tsx useEffect(() => { hasAutoScrolledRef.current = false }, [activeChatId]) ``` Y dentro de `createNewChat`, antes de `setMessages(...)`, agrega: ```tsx hasAutoScrolledRef.current = false ``` --- --- ### Resultado final integrado Te quedaría así en tu componente: ```tsx const scrollRef = useRef<HTMLDivElement>(null) const hasAutoScrolledRef = useRef(false) const scrollToBottom = (behavior: ScrollBehavior = 'auto') => { if (!scrollRef.current) return const scrollContainer = scrollRef.current.querySelector( '[data-radix-scroll-area-viewport]', ) as HTMLDivElement | null if (!scrollContainer) return scrollContainer.scrollTo({ top: scrollContainer.scrollHeight, behavior, }) } useEffect(() => { hasAutoScrolledRef.current = false }, [activeChatId]) useEffect(() => { if (!activeChatId) return if (isLoadingMessages) return if (!hasAutoScrolledRef.current) { requestAnimationFrame(() => { scrollToBottom('auto') hasAutoScrolledRef.current = true }) return } requestAnimationFrame(() => { scrollToBottom('smooth') }) }, [activeChatId, chatMessages, isLoadingMessages]) ``` Y en `createNewChat`: ```tsx const createNewChat = () => { hasAutoScrolledRef.current = false setActiveChatId(undefined) setMessages([ { id: 'welcome', role: 'assistant', content: 'Iniciando una nueva conversación. ¿En qué puedo ayudarte?', }, ]) setInput('') } ``` ### Por qué así * `auto` en la primera carga = abre directo abajo ⚡ * `smooth` después = cuando llegan mensajes nuevos, se siente natural * `requestAnimationFrame` ayuda porque `ScrollArea` de Radix a veces todavía no termina de pintar el viewport cuando corre el effect Hay un detalle extra en tu código: estás usando `RotateCcw` pero no está importado 😅 Arriba te falta: ```tsx import { RotateCcw } from 'lucide-react' ``` Si quieres, te regreso el bloque completo ya pegado sobre tu archivo con el diff exacto.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: Guillermo.Arrieta/acad-ia-2#193