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

React实现点击Div选中状态:复刻Messenger左侧用户列表交互

实现Messenger风格的选中状态切换与消息展示

核心思路是利用React的状态提升——把选中用户的状态放在父组件统一管理,子组件通过props接收状态和回调函数,实现单向数据流驱动UI变化,完全不需要手动操作DOM。

1. 整体组件结构

  • MessengerApp(父组件):维护全局状态,渲染左侧用户列表和右侧消息面板
  • UserList(子组件):渲染用户列表项,传递点击事件给父组件
  • UserItem(子组件):单个用户项,根据选中状态切换样式
  • MessagePanel(子组件):根据选中用户展示对应消息

2. 代码实现

父组件:MessengerApp

import { useState } from 'react';
import UserList from './UserList';
import MessagePanel from './MessagePanel';

// 模拟用户数据
const mockUsers = [
  { id: 1, name: 'Alice', avatar: 'alice.jpg', lastMessage: 'Hey there!' },
  { id: 2, name: 'Bob', avatar: 'bob.jpg', lastMessage: 'What\'s up?' },
  { id: 3, name: 'Charlie', avatar: 'charlie.jpg', lastMessage: 'See you later!' }
];

// 模拟消息数据
const mockMessages = {
  1: [
    { id: 101, sender: 'Alice', text: 'Hey there!', timestamp: '10:00 AM' },
    { id: 102, sender: 'You', text: 'Hi Alice!', timestamp: '10:01 AM' }
  ],
  2: [
    { id: 201, sender: 'Bob', text: 'What\'s up?', timestamp: '9:30 AM' },
    { id: 202, sender: 'You', text: 'Not much, just coding!', timestamp: '9:31 AM' }
  ],
  3: [
    { id: 301, sender: 'Charlie', text: 'See you later!', timestamp: 'Yesterday' },
    { id: 302, sender: 'You', text: 'Sure, catch you soon!', timestamp: 'Yesterday' }
  ]
};

function MessengerApp() {
  // 存储当前选中的用户ID,初始为null
  const [selectedUserId, setSelectedUserId] = useState(null);

  // 处理用户选中的回调函数,更新全局状态
  const handleUserSelect = (userId) => {
    setSelectedUserId(userId);
  };

  // 根据选中ID获取对应消息
  const currentMessages = selectedUserId ? mockMessages[selectedUserId] : [];
  // 获取选中的用户信息
  const selectedUser = mockUsers.find(user => user.id === selectedUserId);

  return (
    <div className="messenger-app">
      <UserList 
        users={mockUsers} 
        selectedUserId={selectedUserId} 
        onUserSelect={handleUserSelect} 
      />
      <MessagePanel 
        messages={currentMessages} 
        selectedUser={selectedUser} 
      />
    </div>
  );
}

export default MessengerApp;

子组件:UserList

import UserItem from './UserItem';

function UserList({ users, selectedUserId, onUserSelect }) {
  return (
    <div className="user-list">
      {users.map(user => (
        <UserItem 
          key={user.id} 
          user={user} 
          // 判断当前用户是否被选中
          isSelected={user.id === selectedUserId} 
          // 点击时调用父组件的回调,传递当前用户ID
          onClick={() => onUserSelect(user.id)} 
        />
      ))}
    </div>
  );
}

export default UserList;

子组件:UserItem

function UserItem({ user, isSelected, onClick }) {
  return (
    <div 
      // 根据isSelected动态添加selected类
      className={`user-item ${isSelected ? 'selected' : ''}`}
      onClick={onClick}
    >
      <img src={user.avatar} alt={user.name} className="user-avatar" />
      <div className="user-info">
        <h3 className="user-name">{user.name}</h3>
        <p className="last-message">{user.lastMessage}</p>
      </div>
    </div>
  );
}

export default UserItem;

子组件:MessagePanel

function MessagePanel({ messages, selectedUser }) {
  // 未选中用户时的提示
  if (!selectedUser) {
    return (
      <div className="message-panel empty">
        <p>Select a user to start chatting</p>
      </div>
    );
  }

  return (
    <div className="message-panel">
      <div className="chat-header">
        <img src={selectedUser.avatar} alt={selectedUser.name} className="chat-avatar" />
        <h2>{selectedUser.name}</h2>
      </div>
      <div className="messages-container">
        {messages.map(message => (
          <div 
            key={message.id} 
            className={`message ${message.sender === 'You' ? 'sent' : 'received'}`}
          >
            <div className="message-text">{message.text}</div>
            <span className="message-time">{message.timestamp}</span>
          </div>
        ))}
      </div>
      <div className="message-input">
        <input type="text" placeholder="Type a message..." />
        <button>Send</button>
      </div>
    </div>
  );
}

export default MessagePanel;

CSS样式(核心选中样式)

.user-item {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  cursor: pointer;
  transition: background-color 0.2s;
}

/* 选中状态的背景色 */
.user-item.selected {
  background-color: #f0f2f5;
}

/* 其他样式省略,可根据需求补充 */

3. 核心逻辑说明

  1. 状态提升:把selectedUserId放在父组件管理,确保所有子组件的选中状态统一,避免状态分散
  2. 单向数据流:子组件通过props接收状态和回调,点击时触发回调通知父组件更新状态,父组件重新渲染所有相关子组件
  3. 无DOM操作:完全通过React状态驱动UI变化,不需要像原生JS那样手动添加/移除类名,符合React的设计理念

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:45:37