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

Next.js实现无刷新更新URL并渲染选中聊天消息

问题描述

我正在使用Next.js开发一款聊天应用,遇到以下需求:

  • 侧边栏(div-1)和中间列(div-2)需始终可见
  • 消息面板(div-3)仅在选中聊天时显示
  • 选中聊天时,URL需更新以反映当前选中的聊天且不跳转页面
  • 聊天消息需根据选中的聊天在消息面板(div-3)中渲染

当前应用布局代码如下:

// MY APP LAYOUT
export default function MyAppLayout({ props }) {
  return (
    <div className="grid grid-cols-12 h-screen w-screen bg-[#F7FAFC] dark:bg-black overflow-hidden">
      {/* Sidebar */}
      <div className="hidden sm:inline sm:col-span-1 relative">
        {/* Sidebar content */}
      </div>

      {/* Div 2 */}
      <div className={`col-span-12 sm:inline sm:col-span-4 px-4 border-l-[0.5px] border-gray-300 dark:border-gray-800 h-screen`}>
        {props.children} {/* Renders different content based on the route all chats , profile page etc*/}
      </div>

      {/* Div 3 */}
      <div className={`col-span-12 sm:inline sm:col-span-7 h-screen dark:bg-black`}>
        {/* Should display selected chat messages with URL reflecting the selection */}
      </div>
    </div>
  );
}

解决方案

1. 核心思路

利用Next.js的路由钩子管理URL状态,通过查询参数(如chatId)标记选中的聊天,以此控制消息面板的显示/隐藏,并触发对应消息内容的渲染。

2. 完整实现代码

第一步:修改布局组件集成路由逻辑

// MY APP LAYOUT
import { useRouter } from 'next/router';

export default function MyAppLayout({ children }) {
  const router = useRouter();
  const selectedChatId = router.query.chatId;

  return (
    <div className="grid grid-cols-12 h-screen w-screen bg-[#F7FAFC] dark:bg-black overflow-hidden">
      {/* Sidebar */}
      <div className="hidden sm:inline sm:col-span-1 relative">
        {/* Sidebar content */}
      </div>

      {/* Div 2 - 始终显示的中间列 */}
      <div className={`col-span-12 sm:inline sm:col-span-4 px-4 border-l-[0.5px] border-gray-300 dark:border-gray-800 h-screen`}>
        {children}
      </div>

      {/* Div 3 - 仅选中聊天时显示消息面板 */}
      {selectedChatId && (
        <div className={`col-span-12 sm:inline sm:col-span-7 h-screen dark:bg-black`}>
          <ChatMessages chatId={selectedChatId} />
        </div>
      )}
    </div>
  );
}

第二步:聊天列表组件处理点击事件

在中间列的聊天列表中,点击聊天项时更新URL查询参数,不触发页面跳转:

// ChatList.jsx
import { useRouter } from 'next/router';

export default function ChatList() {
  const router = useRouter();

  const handleChatSelect = (chatId) => {
    // 更新URL查询参数,shallow: true确保不刷新页面
    router.push(
      {
        pathname: router.pathname,
        query: { ...router.query, chatId },
      },
      undefined,
      { shallow: true }
    );
  };

  return (
    <div className="space-y-2 mt-4">
      {/* 示例聊天项 */}
      <div 
        className="p-3 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
        onClick={() => handleChatSelect('chat_001')}
      >
        张三
      </div>
      <div 
        className="p-3 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
        onClick={() => handleChatSelect('chat_002')}
      >
        李四
      </div>
    </div>
  );
}

第三步:消息渲染组件实现

根据chatId拉取并渲染对应聊天的消息:

// ChatMessages.jsx
import { useEffect, useState } from 'react';

export default function ChatMessages({ chatId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    // 替换为你的实际消息获取逻辑(API请求/状态读取)
    const fetchMessages = async () => {
      const mockData = [
        { id: 1, content: '你好!', isMine: false },
        { id: 2, content: '你好呀!', isMine: true },
      ];
      // 模拟异步请求
      setTimeout(() => setMessages(mockData), 300);
    };

    if (chatId) fetchMessages();
  }, [chatId]);

  return (
    <div className="h-full flex flex-col">
      {/* 聊天头部 */}
      <div className="p-4 border-b border-gray-300 dark:border-gray-800">
        <h3 className="font-medium">与好友聊天</h3>
      </div>
      {/* 消息列表 */}
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map(msg => (
          <div 
            key={msg.id} 
            className={`max-w-[70%] p-3 rounded-lg ${
              msg.isMine ? 'ml-auto bg-blue-100 dark:bg-blue-900' : 'bg-white dark:bg-gray-800'
            }`}
          >
            {msg.content}
          </div>
        ))}
      </div>
      {/* 输入框 */}
      <div className="p-4 border-t border-gray-300 dark:border-gray-800">
        <input 
          type="text" 
          placeholder="输入消息..." 
          className="w-full p-2 rounded-lg border border-gray-300 dark:border-gray-700 dark:bg-gray-900"
        />
      </div>
    </div>
  );
}

关键说明

  • shallow: true是实现无跳转更新URL的核心,它只会更新路由状态和URL,不会触发页面重新渲染或数据重新获取。
  • 通过router.query.chatId的存在与否,控制消息面板的显示/隐藏,满足"仅选中聊天时显示"的需求。
  • 当chatId变化时,ChatMessages组件会自动重新执行消息获取逻辑,保证内容与选中的聊天同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 16:53:11