React实现B站评论Demo
该Demo涉及的技术点
- useState函数(数据驱动视图)
- 子组件的封装
- 条件判断
- 回调函数的封装
1、评论数据
{"list": [{"rpid": 3,"user": {"uid": "13258165","avatar": "http://toutiao.itheima.net/resources/images/98.jpg","uname": "周杰伦"},"content": "哎哟,不错哦","ctime": "10-18 08: 15","like": 126},{"rpid": 2,"user": {"uid": "36080105","avatar": "http://toutiao.itheima.net/resources/images/98.jpg","uname": "许嵩"},"content": "我寻你千百度 日出到迟暮","ctime": "11-13 11: 29","like": 88},{"rpid": 1,"user": {"uid": "30009257","avatar": "http://toutiao.itheima.net/resources/images/98.jpg","uname": "前端"},"content": "前端真好玩","ctime": "10-19 09: 00","like": 66}]
}
2、具体实现
- 获取json中的list数据 (读取本地文件 / 读取接口)
const JsonReader = () => {// 定义一个数组状态来存储 JSON 数据const [ commentList, setCommentList ] = useState([]);// 使用 useEffect 钩子来加载 JSON 文件useEffect(() => {// 使用 fetch API 读取 JSON 文件 , 这里需要注意,json文件读取的根路径是public路径fetch('_db.json').then(response => {if (!response.ok) {throw new Error('Network response was not ok');}return response.json(); // 解析 JSON 数据}).then(data => {// console.log(data)// 将解析后的 JSON 数据存储到 list 数组中setCommentList(_.orderBy(data.list, 'like', 'desc'));}).catch(error => {console.error('There has been a problem with your fetch operation:', error);});}, []); // 空依赖数组表示只在组件挂载时执行一次return {commentList, setCommentList}
}// 封装请求数据的Hook
function useGetList () {// 获取接口数据渲染const [commentList, setCommentList] = useState([])useEffect(() => {// 请求数据async function getList () {// axios请求数据const res = await axios.get(' http://localhost:3004/list')setCommentList(res.data)}getList()}, [])return {commentList,setCommentList}
}
- 封装用户信息、导航栏
// 当前登录用户信息
const user = {// 用户iduid: '30009257',// 用户头像avatar,// 用户昵称uname: '黑马前端',
}
// 导航 Tab 数组
const tabs = [{ type: 'hot', text: '最热' },{ type: 'time', text: '最新' },
]
- 封装单个评论组件
// 封装Item组件
function Item ({ item, onDel }) {return (<div className="reply-item">{/* 头像 */}<div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"alt=""src={item.user.avatar}/></div></div><div className="content-wrap">{/* 用户名 */}<div className="user-info"><div className="user-name">{item.user.uname}</div></div>{/* 评论内容 */}<div className="root-reply"><span className="reply-content">{item.content}</span><div className="reply-info">{/* 评论时间 */}<span className="reply-time">{item.ctime}</span>{/* 评论数量 */}<span className="reply-time">点赞数:{item.like}</span>{/* 条件:user.id === item.user.id */}{user.uid === item.user.uid &&<span className="delete-btn" onClick={() => onDel(item.rpid)}>删除</span>}</div></div></div></div>)
}
- 最终实现
const App = () => {// 渲染评论列表// 1. 使用useState维护list// const [commentList, setCommentList] = useState(_.orderBy(List, 'like', 'desc'))// const { commentList, setCommentList } = useGetList()const { commentList, setCommentList } = JsonReader()// 删除功能const handleDel = (id) => {console.log(id)// 对commentList做过滤处理setCommentList(commentList.filter(item => item.rpid !== id))}// tab切换功能// 1. 点击谁就把谁的type记录下来// 2. 通过记录的type和每一项遍历时的type做匹配 控制激活类名的显示const [type, setType] = useState('hot')const handleTabChange = (type) => {console.log(type)setType(type)// 基于列表的排序if (type === 'hot') {// 根据点赞数量排序 // lodashsetCommentList(_.orderBy(commentList, 'like', 'desc'))} else {// 根据创建时间排序setCommentList(_.orderBy(commentList, 'ctime', 'desc'))}}// 发表评论const [content, setContent] = useState('')const inputRef = useRef(null)const handlPublish = () => {setCommentList([...commentList,{rpid: uuidV4(), // 随机iduser: {uid: '30009257',avatar,uname: '黑马前端',},content: content,ctime: dayjs(new Date()).format('MM-DD hh:mm'), // 格式化 月-日 时:分like: 66,}])// 1. 清空输入框的内容setContent('')// 2. 重新聚焦 dom(useRef) - focusinputRef.current.focus()}return (<div className="app">{/* 导航 Tab */}<div className="reply-navigation"><ul className="nav-bar"><li className="nav-title"><span className="nav-title-text">评论</span>{/* 评论数量 */}<span className="total-reply">{10}</span></li><li className="nav-sort">{/* 高亮类名: active */}{tabs.map(item =><spankey={item.type}onClick={() => handleTabChange(item.type)}className={classNames('nav-item', { active: type === item.type })}>{item.text}</span>)}</li></ul></div><div className="reply-wrap">{/* 发表评论 */}<div className="box-normal">{/* 当前用户头像 */}<div className="reply-box-avatar"><div className="bili-avatar"><img className="bili-avatar-img" src={avatar} alt="用户头像" /></div></div><div className="reply-box-wrap">{/* 评论框 */}<textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"ref={inputRef}value={content}onChange={(e) => setContent(e.target.value)}/>{/* 发布按钮 */}<div className="reply-box-send"><div className="send-text" onClick={handlPublish}>发布</div></div></div></div>{/* 评论列表 */}<div className="reply-list">{/* 评论项 */}{commentList.map(item => <Item key={item.id} item={item} onDel={handleDel} />)}</div></div></div>)
}
最终效果: