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. 核心逻辑说明
- 状态提升:把
selectedUserId放在父组件管理,确保所有子组件的选中状态统一,避免状态分散 - 单向数据流:子组件通过props接收状态和回调,点击时触发回调通知父组件更新状态,父组件重新渲染所有相关子组件
- 无DOM操作:完全通过React状态驱动UI变化,不需要像原生JS那样手动添加/移除类名,符合React的设计理念
内容的提问来源于stack exchange,提问作者tudk
相关产品推荐
相关产品推荐

