挑战用React封装100个组件【007】

项目地址
https://github.com/hismeyy/react-component-100

组件描述
今天的组件是用来展示聊天列表,或者论坛内容列表的组件。配合挑战006的时候开发的组件,可以显示用户的具体信息。

样式展示

在这里插入图片描述
在这里插入图片描述

前置依赖

今天,我分享的组件,需要用到的依赖有:

  1. react-icons(提供图标)
  2. InfoCard(提供查看用户详细信息)

安装 react-icons

# 使用 npm
npm install react-icons# 或者使用 yarn
yarn add react-icons

使用的话,大家可以看这个网站。大家进去可以找需要的图标。具体使用里面有介绍,非常简单。
react-icons 图标

InfoCard
这个组件的话,大家可以看 挑战用React封装100个组件【006】 文章。

代码展示

InfoCard.tsx
import './ChatList.css';
import { useState, useRef, useCallback } from 'react';
import { AiOutlineLike, AiOutlineMessage, AiOutlineStar } from "react-icons/ai";
import { chatData, ChatItem } from './data';
import InfoCard from '../card/infoCard04/InfoCard';const ChatList = () => {// 用于管理悬停交互和信息卡片显示的状态const [hoveredUser, setHoveredUser] = useState<ChatItem | null>(null);const [infoCardPosition, setInfoCardPosition] = useState({ x: 0, y: 0 });const [isCardFading, setIsCardFading] = useState(false);// 用于管理动画超时的引用const timeoutRef = useRef<number | null>(null);// 用于跟踪鼠标是否在卡片区域内const isMouseInCardRef = useRef(false);/*** 处理头像的鼠标进入事件* 显示信息卡片并计算其正确位置*/const handleAvatarMouseEnter = (e: React.MouseEvent<HTMLDivElement>, item: ChatItem) => {if (timeoutRef.current) {window.clearTimeout(timeoutRef.current);timeoutRef.current = null;}setIsCardFading(false);// 根据头像位置计算信息卡片位置const rect = e.currentTarget.getBoundingClientRect();setInfoCardPosition({x: rect.left + window.scrollX,y: rect.bottom + window.scrollY});setHoveredUser(item);};/*** 检查鼠标是否移动到了InfoCard上* 通过检查鼠标当前位置和InfoCard的位置关系来判断*/const checkIfMouseMovingToCard = useCallback((e: MouseEvent) => {const cardElement = document.querySelector('.info-card-container');if (!cardElement) return false;const cardRect = cardElement.getBoundingClientRect();const mouseX = e.clientX;const mouseY = e.clientY;// 扩大判定区域,给予用户更大的移动空间const expandedRect = {left: cardRect.left - 20,right: cardRect.right + 20,top: cardRect.top - 20,bottom: cardRect.bottom + 20};return mouseX >= expandedRect.left && mouseX <= expandedRect.right && mouseY >= expandedRect.top && mouseY <= expandedRect.bottom;}, []);/*** 处理头像和信息卡片的鼠标离开事件*/const handleMouseLeave = (e: React.MouseEvent) => {// 如果鼠标正在往InfoCard方向移动,不触发隐藏if (checkIfMouseMovingToCard(e.nativeEvent)) {return;}// 如果鼠标已经在卡片内,不触发隐藏if (isMouseInCardRef.current) {return;}setIsCardFading(true);timeoutRef.current = window.setTimeout(() => {if (!isMouseInCardRef.current) {setHoveredUser(null);setIsCardFading(false);}}, 300);};/*** 处理信息卡片的鼠标进入事件*/const handleInfoCardMouseEnter = () => {isMouseInCardRef.current = true;if (timeoutRef.current) {window.clearTimeout(timeoutRef.current);timeoutRef.current = null;}setIsCardFading(false);};/*** 处理信息卡片的鼠标离开事件*/const handleInfoCardMouseLeave = () => {isMouseInCardRef.current = false;setIsCardFading(true);timeoutRef.current = window.setTimeout(() => {if (!isMouseInCardRef.current) {setHoveredUser(null);setIsCardFading(false);}}, 300);};/*** 处理关注按钮点击事件*/const handleFollow = () => {console.log('关注用户:', hoveredUser?.userName);};/*** 处理发送消息按钮点击事件*/const handleMessage = () => {console.log('发送消息给:', hoveredUser?.userName);};return (<div className="chat-list">{/* 聊天项目列表 */}<ul className="chat-items">{chatData.map((item: ChatItem) => (<li key={item.id} className="chat-item">{/* 用户信息区域 */}<div className='chat-info'><div className='user-avatar'onMouseEnter={(e) => handleAvatarMouseEnter(e, item)}onMouseLeave={handleMouseLeave}><img src={item.userAvatar} alt={item.avatarAlt} /></div><div className='user-info'><h6 className='user-name'>{item.userName}</h6><p className='send-time'>{item.sendTime}</p></div></div>{/* 消息内容 */}<div className='chat-content'><p>{item.content}</p></div>{/* 互动按钮 */}<div className='chat-functions'><div className='like'><AiOutlineLike />{item.likes}</div><div className='comment'><AiOutlineMessage />{item.comments}</div><div className='collect'><AiOutlineStar />{item.collections}</div></div></li>))}</ul>{/* 悬停信息卡片 */}{hoveredUser && (<div className={`info-card-container ${isCardFading ? 'fade-out' : ''}`}style={{position: 'absolute',left: `${infoCardPosition.x}px`,top: `${infoCardPosition.y + 10}px`,zIndex: 1000}}onMouseEnter={handleInfoCardMouseEnter}onMouseLeave={handleInfoCardMouseLeave}><InfoCardavatarUrl={hoveredUser.userAvatar}avatarAlt={hoveredUser.avatarAlt}name={hoveredUser.userName}description={hoveredUser.description}labels={hoveredUser.labels}isVerified={hoveredUser.isVerified}onFollow={handleFollow}onMessage={handleMessage}/></div>)}</div>);
};export default ChatList;
InfoCard.css
/* 聊天列表容器 */
.chat-list {width: 100%;background-color: #FFFFFF;border-radius: 10px;padding: 10px 30px;box-sizing: border-box;
}/* 聊天项目列表 */
.chat-list .chat-items {all: unset;
}/* 单个聊天项目 */
.chat-list .chat-items .chat-item {display: flex;flex-direction: column;justify-content: left;margin: 20px 0;
}/* 聊天项目分隔线 */
.chat-list .chat-items .chat-item::after {content: '';display: block;width: 100%;height: 1px;background-color: #e6e6e6;margin-top: 20px;
}/* 用户信息区域 */
.chat-list .chat-items .chat-item .chat-info {height: 50px;display: flex;justify-content: left;
}/* 用户头像 */
.chat-list .chat-items .chat-item .chat-info .user-avatar {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;cursor: pointer;transition: transform 0.2s ease;
}/* 头像悬停效果 */
.chat-list .chat-items .chat-item .chat-info .user-avatar:hover {transform: scale(1.05);
}/* 头像图片 */
.chat-list .chat-items .chat-item .chat-info .user-avatar img {width: 100%;height: 100%;object-fit: cover;
}/* 用户信息容器 */
.chat-list .chat-items .chat-item .chat-info .user-info {height: 100%;display: flex;flex-direction: column;justify-content: center;margin-left: 20px;gap: 5px;
}/* 用户名称 */
.chat-list .chat-items .chat-item .chat-info .user-info .user-name {all: unset;display: block;font-size: 16px;font-weight: bold;
}/* 发送时间 */
.chat-list .chat-items .chat-item .chat-info .user-info .send-time {all: unset;display: block;font-size: 12px;color: #B3B3B3
}/* 聊天内容区域 */
.chat-list .chat-items .chat-item .chat-content {margin-top: 15px;
}/* 聊天内容文本 */
.chat-list .chat-items .chat-item .chat-content p {all: unset;display: block;font-size: 14px;line-height: 1.5;word-break: break-all;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2;line-clamp: 2;box-orient: vertical;cursor: pointer;
}/* 互动功能区域 */
.chat-list .chat-items .chat-item .chat-functions {display: flex;gap: 20px;margin-top: 20px;justify-content: left;font-size: 14px;color: #B3B3B3;
}/* 互动按钮 */
.chat-list .chat-items .chat-item .chat-functions div {display: flex;gap: 2px;align-items: center;cursor: pointer;color: #292929;transition: all 0.3s ease
}/* 互动按钮悬停效果 */
.chat-list .chat-items .chat-item .chat-functions div:hover {color: #f08a5d;
}/* 信息卡片淡入动画 */
@keyframes fadeIn {from {opacity: 0;transform: translateY(-10px);}to {opacity: 1;transform: translateY(0);}
}/* 信息卡片淡出动画 */
@keyframes fadeOut {from {opacity: 1;transform: translateY(0);}to {opacity: 0;transform: translateY(-10px);}
}/* 信息卡片容器 */
.info-card-container {pointer-events: auto;animation: fadeIn 0.2s ease-out forwards;
}.info-card-container.fade-out {animation: fadeOut 0.2s ease-out forwards;
}

使用

App.tsx
import './App.css'
import ChatList from './components/chatList/ChatList';function App() {return (<><div className="App"><ChatList /></div></>);
}export default App

数据

除了代码之外,我们还需要数据!我放在了data.ts

export interface ChatItem {id: number;userAvatar: string;avatarAlt: string;userName: string;sendTime: string;content: string;likes: number;comments: number;collections: number;description: string;labels: string[];isVerified: boolean;
}export const chatData: ChatItem[] = [{id: 1,userAvatar: "https://randomuser.me/api/portraits/men/1.jpg",avatarAlt: "张明的头像",userName: "张明",sendTime: "今天 08:30",content: "刚刚参加完一场很棒的技术分享会,讲的是React 18的新特性。Concurrent Mode和Server Components真的让人印象深刻,感觉未来的前端开发会更加有趣!",likes: 156,comments: 32,collections: 18,description: "资深前端工程师 / React 技术专家",labels: ["React", "TypeScript", "前端架构", "性能优化", "开源贡献者"],isVerified: true},{id: 2,userAvatar: "https://randomuser.me/api/portraits/women/2.jpg",avatarAlt: "李小云的头像",userName: "李小云",sendTime: "今天 09:15",content: "分享一个我最近在项目中遇到的性能优化问题:大量数据渲染导致页面卡顿。通过使用虚拟列表和React.memo()成功解决,页面加载速度提升了80%。欢迎交流讨论~",likes: 234,comments: 45,collections: 28,description: "高级前端开发 / 性能优化专家",labels: ["性能优化", "React", "虚拟列表", "前端架构"],isVerified: true},{id: 3,userAvatar: "https://randomuser.me/api/portraits/men/3.jpg",avatarAlt: "王大力的头像",userName: "王大力",sendTime: "今天 10:42",content: "推荐一本超棒的技术书籍《深入浅出React和Redux》,对于想深入学习React的同学来说是一本不可多得的好书。书中的案例都很实用,概念讲解也非常清晰。",likes: 89,comments: 15,collections: 42,description: "技术作家 / React 培训讲师",labels: ["技术写作", "React", "Redux", "技术分享"],isVerified: false},{id: 4,userAvatar: "https://randomuser.me/api/portraits/women/4.jpg",avatarAlt: "陈佳慧的头像",userName: "陈佳慧",sendTime: "今天 11:20",content: "今天终于解决了困扰团队一周的Bug!原来是在处理异步请求时没有正确处理竞态条件,导致数据更新错乱。分享一下解决方案:使用AbortController和useEffect的cleanup函数完美解决了这个问题。",likes: 312,comments: 56,collections: 33,description: "全栈工程师 / React Native 专家",labels: ["React", "React Native", "移动开发", "全栈开发"],isVerified: true},{id: 5,userAvatar: "https://randomuser.me/api/portraits/men/5.jpg",avatarAlt: "刘技术的头像",userName: "刘技术",sendTime: "今天 12:05",content: "最近在研究微前端架构,感觉qiankun框架真的很强大。已经成功将我们的老项目逐步迁移到微前端架构,既保证了系统的稳定性,又提高了团队的开发效率。有同样经历的同学吗?",likes: 178,comments: 43,collections: 25,description: "架构师 / 微前端专家",labels: ["微前端", "架构设计", "qiankun", "模块联邦"],isVerified: true},{id: 6,userAvatar: "https://randomuser.me/api/portraits/women/6.jpg",avatarAlt: "赵晓晓的头像",userName: "赵晓晓",sendTime: "今天 13:30",content: "发现一个超实用的VS Code插件:GitHub Copilot!AI辅助编程真的太强大了,特别是在写一些重复性的代码时效率提升明显。推荐给大家!",likes: 267,comments: 89,collections: 54,description: "开发工具专家 / 效率工程师",labels: ["开发工具", "VS Code", "AI编程", "效率提升"],isVerified: false},{id: 7,userAvatar: "https://randomuser.me/api/portraits/men/7.jpg",avatarAlt: "孙小明的头像",userName: "孙小明",sendTime: "今天 14:15",content: "今天做了一个有趣的小实验:用React + Three.js开发了一个3D数据可视化组件。效果出乎意料的好,准备开源出来。感兴趣的同学请留言,我会把仓库地址分享出来。",likes: 423,comments: 98,collections: 76,description: "3D可视化专家 / React 开发者",labels: ["Three.js", "WebGL", "数据可视化", "React"],isVerified: true},{id: 8,userAvatar: "https://randomuser.me/api/portraits/women/8.jpg",avatarAlt: "周雪的头像",userName: "周雪",sendTime: "今天 15:00",content: "作为一名前端开发,最近开始学习TypeScript,真的改变了我的编程习惯。类型系统不仅让代码更安全,重构时也更有信心。强烈建议还没入门的同学抓紧学起来!",likes: 345,comments: 67,collections: 45,description: "前端开发工程师 / TypeScript 布道者",labels: ["TypeScript", "前端开发", "代码质量", "最佳实践"],isVerified: false}
];

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482236.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汉字Unicode编码相互转换API集成指南

汉字Unicode编码相互转换API集成指南 引言 在国际化的背景下&#xff0c;字符编码的统一变得尤为重要。Unicode作为一种通用字符集标准&#xff0c;能够支持全球几乎所有的语言文字&#xff0c;包括复杂的汉字系统。对于开发人员来说&#xff0c;掌握如何在不同的编码格式之间…

[Linux] 进程间通信——匿名管道命名管道

标题&#xff1a;[Linux] 进程间通信——匿名管道&&命名管道 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、进程间通信 二、进程间通信的方案——匿名管道 &#xff08;1&#xff09;匿名管道的原理 &#xff08;2&#xff09;使用匿名管道 三、进…

一体化数据安全平台uDSP 入选【年度创新安全产品 TOP10】榜单

近日&#xff0c;由 FreeBuf 主办的 FCIS 2024 网络安全创新大会在上海隆重举行。大会现场揭晓了第十届 WitAwards 中国网络安全行业年度评选获奖名单&#xff0c;该评选自 2015 年举办以来一直饱受赞誉&#xff0c;备受关注&#xff0c;评选旨在以最专业的角度和最公正的态度&…

pycharm链接neo4j(导入文件)

1.新建csv文件 2.写入文件 3.运行代码 import csv from py2neo import Graph, Node, Relationship# 连接到Neo4j数据库&#xff0c;使用Bolt协议 graph Graph("bolt://localhost:7687", auth("neo4j", "password"))# 读取CSV文件 with open(…

vscode ctrl+/注释不了css

方式一.全部禁用插件排查问题. 方式二.打开首选项的json文件,注释掉setting.json,排查是哪一行配置有问题. 我的最终问题:需要将 "*.vue": "vue",改成"*.vue": "html", "files.associations": { // "*.vue": &qu…

TCP三次握手与四次挥手(TCP重传机制,2MSL)超详细!!!计算机网络

本篇是关于3次握手和四次挥手的详细解释~ 如果对你有帮助&#xff0c;请点个免费的赞吧&#xff0c;谢谢汪。&#xff08;点个关注也可以&#xff01;&#xff09; 如果以下内容需要补充和修改&#xff0c;请大家在评论区多多交流~。 目录 1. TCP头部&#xff1a; 2. 三次握手…

单片机学习笔记 15. 串口通信(理论)

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

C#中switch语句使用

编写一个程序&#xff0c;使用switch语句将用户输入的分数转换成等级&#xff0c;如表 private static void Main(string[] args) { Console.WriteLine("请输入分数&#xff1a;"); int score int.Parse(Console.ReadLine()); switch (score) …

[网络安全]sqli-labs Less-5 解题详析

[网络安全]Less-5 GET - Double Injection - Single quotes - String:双注入GET单引号字符型注入 判断注入类型判断注入点个数查库名&#xff08;爆破&#xff09; left函数抓包查库名&#xff08;双查询注入&#xff09; 原理实例查库名&#xff08;extractvalue函数&#xff…

pyspark实现基于协同过滤的电影推荐系统

最近在学一门大数据的课&#xff0c;课程要求很开放&#xff0c;任意做一个大数据相关的项目即可&#xff0c;不知道为什么我就想到推荐算法&#xff0c;一直到着手要做之前还没有新的更好的来代替&#xff0c;那就这个吧。 推荐算法 推荐算法的发展由来已久&#xff0c;但和…

python股票数据分析(Pandas)练习

需求&#xff1a; 使用pandas读取一个CSV文件&#xff0c;文件内容包括股票名称、价格和交易量。完成以下任务&#xff1a; 找出价格最高的股票&#xff1b; 计算总交易量&#xff1b; 绘制价格折线图。 代码实现&#xff1a; import pandas as pd import matplotlib.pyplot …

利用Python爬虫精准获取淘宝商品详情的深度解析

在数字化时代&#xff0c;数据的价值日益凸显&#xff0c;尤其是在电子商务领域。淘宝作为中国最大的电商平台之一&#xff0c;拥有海量的商品数据&#xff0c;对于研究市场趋势、分析消费者行为等具有重要意义。本文将详细介绍如何使用Python编写爬虫程序&#xff0c;精准获取…

K8s调度器扩展(scheduler)

1.K8S调度器 筛选插件扩展 为了熟悉 K8S调度器扩展步骤&#xff0c;目前只修改 筛选 插件 准备环境&#xff08;到GitHub直接下载压缩包&#xff0c;然后解压&#xff0c;解压要在Linux系统下完成&#xff09; 2. 编写调度器插件代码 在 Kubernetes 源代码目录下编写调度插件…

领养我的宠物:SpringBoot开发指南

第2章 开发环境与技术 本章节对开发宠物领养系统需要搭建的开发环境&#xff0c;还有宠物领养系统开发中使用的编程技术等进行阐述。 2.1 Java语言 Java语言是当今为止依然在编程语言行业具有生命力的常青树之一。Java语言最原始的诞生&#xff0c;不仅仅是创造者感觉C语言在编…

Permute for Mac 媒体文件格式转换软件 安装教程【音视频图像文件转换,简单操作,轻松转换,提高效率】

Mac分享吧 文章目录 Permute for Mac 格式转换软件 效果图展示一、Permute 格式转换软件 Mac电脑版——v3.11.15⚠️注意事项&#xff1a;1️⃣&#xff1a;下载软件2️⃣&#xff1a;安装软件2.1 左侧安装包拖入右侧文件夹中&#xff0c;等待安装完成&#xff0c;运行软件2.2…

【Android】EventBus的使用及源码分析

文章目录 介绍优点基本用法线程模式POSTINGMAINMAIN_ORDEREDBACKGROUNDASYNC 黏性事件 源码注册getDefault()registerfindSubscriberMethods小结 postpostStickyunregister 介绍 优点 简化组件之间的通信 解耦事件发送者和接收者在 Activity、Fragment 和后台线程中表现良好避…

原子类、AtomicLong、AtomicReference、AtomicIntegerFieldUpdater、LongAdder

原子类 JDK提供的原子类&#xff0c;即Atomic*类有很多&#xff0c;大体可做如下分类&#xff1a; 形式类别举例Atomic*基本类型原子类AtomicInteger、AtomicLong、AtomicBooleanAtomic*Array数组类型原子类AtomicIntegerArray、AtomicLongArray、AtomicReferenceArrayAtomic…

【Electron学习笔记(三)】Electron的主进程和渲染进程

Electron的主进程和渲染进程 Electron的主进程和渲染进程前言正文1、主进程2、渲染进程3、Preload 脚本3.1 在项目目录下创建 preload.js 文件3.2 在 main.js 文件下创建路径变量并将 preload.js 定义为桥梁3.3 在 preload.js 文件下使用 electron 提供的contextBridge 模块3.4…

FFmpeg一些常用的命令

官网&#xff1a;https://ffmpeg.org/ 官网下载&#xff1a;https://ffmpeg.org/download.html 官网下载源码&#xff1a;https://www.ffmpeg.org/releases/ FFmpeg 实用命令 — FFmpeg 教程 文档 一、参数 1.1 FFmpeg 常用参数 参数说明备注-i filename指定输入文件&#…

JAVA篇08 —— String类

欢迎来到我的主页&#xff1a;【一只认真写代码的程序猿】 本篇文章收录于专栏【小小爪哇】 如果这篇文章对你有帮助&#xff0c;希望点赞收藏加关注啦~ 目录 1 String概述 1.1 String特性 1.2 String常用方法 2 StringBuffer类 2.1 String与StringBuffer互转 2.2 Stri…