React hooks实现聊天室

功能说明

  1. 聊天室样式排版
  2. 下拉刷新功能在React版本聊天区域实现
  3. 在初始状态和新内容发送时在React版本实现滚动条置底功能
  4. 发送内容或刷新页面时去更新数据,没有轮询或者socket来实时更新

简单看下效果

请添加图片描述

功能开发

样式实现

通过css伪类给聊天框添加尖角

<div className={style.chatRoom}>
    <div className={style.chatContent}>
        {chatList?.map((chatItem: allType) => {
            if (chatItem?.speaker == 'e') {
                return (
                    <div className={style.chatLeft}>
                         <img src={(showLeft == 'e' ? chatItem?.expertImgUrl : chatItem?.userImgUrl) ?? avater} alt="" />
                         <div className={style.info}>
                             <div className={style.name}>{showLeft == 'e' ? chatItem?.expertName : chatItem?.userName}</div>
                             <div className={style.textCon}>
                                 <div className={style.text}>{chatItem?.content}</div>
                             </div>
                         </div>
                     </div>
                )
            } else {
                return (
                    <div className={style.chatRight}>
                        <img src={(showLeft == 'e' ? chatItem?.userImgUrl : chatItem?.expertImgUrl) ?? avater} alt="" />
                        <div className={style.info}>
                            <div className={style.name}>{showLeft == 'e' ? chatItem?.userName : chatItem?.expertName}</div>
                            <div className={style.textCon}>
                                <div className={style.text}>{chatItem?.content}</div>
                            </div>
                        </div>
                    </div>
                )
            }
        })}
    </div>
    <div className={style.inputArea}>
        <Input placeholder="请输入内容" size="large" />
        <Button size="large" type="primary">发送</Button>
    </div>
</div>
.chatRoom{
    padding: 14px;
    margin-top: 20px;
    border: 1px solid #dfefff;
    border-radius: 4px;
    background: rgba(239, 247, 260, .5);
    .chatContent{
      height: 500px;
      overflow: auto;
      .chatLeft{
        display: flex;
        margin-bottom: 20px;
        .text::before {
          content: "";
          position: absolute;
          top: 10px;
          bottom: 0;
          left: -6.3px;
          border: 1px solid #EEEEEE;
          border-radius: 3px;
          width: 10px;
          height: 10px;
          background: #fff;
          transform: rotate(45deg);
          border-right: transparent;
          border-top: transparent;
      }
      }
      .chatRight{
        display: flex;
        flex-direction: row-reverse;
        margin-bottom: 20px;
        .info{
          text-align: right;
        }
        .text::after {
          content: "";
          position: absolute;
          top: 10px;
          bottom: 0;
          right: -6.3px;
          border: 1px solid #EEEEEE;
          border-radius: 3px;
          width: 10px;
          height: 10px;
          background: #fff;
          transform: rotate(45deg);
          border-left: transparent;
          border-bottom: transparent;
      	}
      }
      img{
        width: 45px;
        height: 45px;
        border-radius: 50%;
      }
      .info{
          margin: 0 12px;
          .name{
            font-size: 16px;
          }
          .textCon{
            position: relative;
            .text{
              position: relative;
              display: inline-block;
              max-width: 350px;
              padding: 8px 12px;
              border: 1px solid #EEEEEE;
              border-radius: 5px;
              background: #FFFFFF;
            }
          }   
      }
    }
    .inputArea{
      position: relative;
      button{
        position: absolute;
        right: 0;
      }
    }
  }
实现滚动条置底 (默认状态以及发送消息时)

页面初始化后,根据接口返回值,判断当前页码是否为1,将聊天室的scrollTop值设置为scrollHeight来实现置底;
发送成功后,若当前为第一页,直接获取最新数据,否则,将分页重置到第一页,再去获取,以触发置底设置

import React, {useRef, useState, useEffect} from "react"

const ChatRoom = () => {
	const [chatList, setChatList] = useState<allType[]>([])
	const [pageNo, setPageNo] = useState<number>(1)
	const [inputVal, setInputVal] = useState<string>('')
	const content = useRef<HTMLDivElement>(null)
	const sendMessage = async () => {
        await addChat({
            content: inputVal
        })
        setInputVal('')
        if(pageNo == 1){
        	// 发送时若为第一页,无法触发update,需手动获取数据
            getChatList()
        }else{
        	// 发送消息时,若当前页不是第一页,手动设置,以触发滚动条置底设置
            setPageNo(1)
        }  
    }
	const getChatList = async () => {
        const res = await fentchChatList({
            pageNo,
            pageSize: 10
        })
        let listObj = []
        if(res.obj.pageNum != 1){
            listObj = [
                ...res.obj.list,
                ...chatList
            ]
        }else{
            listObj = res.obj.list
        }
        setChatList(listObj)
        // 当pageNum为第一页时,设置scrollTop,使滚动条置底
        if(content && content.current && res.obj.pageNum == 1){
            content.current.scrollTop = content.current?.scrollHeight
        }
    }
	useEffect(() => {
	   getChatList()
	}, [pageNo])
	return (
		<div className={style.chatRoom}>
		    <div 
		    	className={style.chatContent}
		    	ref={content}
		    >内容见样式实现</div>
		    <div className={style.inputArea}>
		    	<Input placeholder="请输入内容" size="large" value={inputVal} onChange={changeInput} />
                <Button size="large" type="primary" onClick={sendMessage}>发送</Button>
		    </div>
		</div>
	)
}
下拉加载功能

scrollTop为0时,判断是否还有新数据,若有则去获取并拼接在上一页数据之前,否则文字提示已为全部内容

import React, {useRef, useState, useEffect} from "react"

const ChatRoom = () => {
	const [chatList, setChatList] = useState<allType[]>([])
	const [pageNo, setPageNo] = useState<number>(1)
	const [inputVal, setInputVal] = useState<string>('')
	// 记录当前是否还有历史聊天内容,避免无意义的调用
	const [hasMore, setHasMore] = useState<boolean>(true)
	// 加载数据时显示loading效果
	const [chatLoding, setChatLoading] = useState(false)
	const content = useRef<HTMLDivElement>(null)
	const sendMessage = async () => {
        await addChat({
            content: inputVal
        })
        setInputVal('')
        if(pageNo == 1){
            getChatList()
        }else{
            setPageNo(1)
            // 发送消息是 重置setHasMore数据
            setHasMore(true)
        }  
    }
	const getChatList = async () => {
		// 打开loading
		setChatLoading(true)
        const res = await fentchChatList({
            pageNo,
            pageSize: 10
        })
        let listObj = []
        if(res.obj.pageNum != 1){
            listObj = [
                ...res.obj.list,
                ...chatList
            ]
        }else{
            listObj = res.obj.list
        }
        setChatList(listObj)
        if(content && content.current && res.obj.pageNum == 1){
            content.current.scrollTop = content.current?.scrollHeight
        }
        // 根据接口返回的数据 设置是否还有历史数据
        setHasMore(res.obj.hasNextPage)
        //关闭loading
        setChatLoading(false)
    }
    useEffect(() => {
    	// 监听聊天区域的scroll事件
        content?.current?.addEventListener('scroll', () => {
        	// 触发时,判断滚动条是否在页面顶部,去获取历史数据
            if(content?.current?.scrollTop == 0){
            	// 这里使用回调形式修改pageNo,因为方法在update时不会更新,直接设置拿不到最新的state
                setPageNo((page) => {
                    return page + 1
                })
            }
        })
    }, [])
	useEffect(() => {
	   getChatList()
	}, [pageNo])
	return (
		<div className={style.chatRoom}>
		    <div 
		    	className={style.chatContent}
		    	ref={content}
		    >
		    {!hasMore && pageNo != 1 && <div className={style.chatLoading}>没有更多内容了</div>}
            {chatLoding && <div className={style.chatLoading}><Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} /></div>}
		    内容见样式实现
		    </div>
		    <div className={style.inputArea}>内容见样式实现</div>
		</div>
	)
}

至此,功能基本实现。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值