尽可能的优化,避免长聊天记录导致的卡顿
拆分组件,隔离组件状态渲染,可快速多次重复提交数据
index.js
import React, { useContext, useState, useCallback, useRef } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import chatgpt from './chatgpt.png';
import user from './user.png';
import { useEffect } from 'react';
import { PageRouterContext } from "../../App";
import config from "../../config/config";
import axios from 'axios';
export default function App1() {
const [comments, setComments] = useState([]);
const [userName, setUserName] = useState("");
const list_container_id = useRef(null);
const [count, setCount] = useState(0);
const changeRoute = useContext(PageRouterContext);
const navigateTo = (changeRoute, id) => {
changeRoute({ id: id });
};
const getList = (title) => {
return new Promise((resolve) => {
axios.post('/search/send', {
message: title
}).then((response) => {
if (Array.isArray(response.data.choices)) {
console.log('请求成功', response);
resolve(response.data.choices);
} else {
alert('程序错误');
}
// 请求成功
}).catch((error) => {
// 请求失败,
console.log(error);
});
})
}
const scrollBottom = () => {
if (!list_container_id.current) {
return;
}
setTimeout(() => {
list_container_id.current.scrollTop = list_container_id.current.scrollHeight
}, 0);
}
const updateScroll = useCallback(() => {
scrollBottom()
})
const addComment = async (e) => {
if (userName.trim() === '') {
alert('请输入问题');
return;
}
let index = comments.length;
comments.push({
id: Math.random(),
name: userName,
contents: []
});
setComments(comments);
setCount(count + 1);
setTimeout(async () => {
let responseList = await getList(userName);
comments[index].contents = responseList;
setComments(comments);
setUserName('');
setCount(0);
}, 0);
}
const renderList = () => {
return comments.length === 0 ?
(<div className='no-comment'>暂无问题,快去提问吧~</div>)
: (
<div
ref={(el) => {
list_container_id.current = el;
}}
id="list_container_id"
className="list_container"
>
<ul style={{ color: 'white' }}>
{comments.map((item, index) => (
<li key={item.id} style={{ color: 'white' }}>
{
item.name ? (
<div
className='quiz'>
<img className='quiz_avatar' src={user} />
<span style={{ marginLeft: 8 }}>提问: {item.name}</span>
</div>
) : null
}
{
item.contents.length ? (
<div
className='answer'>
<img className='quiz_avatar' src={chatgpt} />
<ClickFingerTextBoard dataList={item.contents} index={index} updateScroll={updateScroll} />
</div>
) : <div className='answer'>加载中...</div>
}
</li>
))}
</ul>
</div>
)
}
const handleForm = (e) => {
setUserName(e.target.value)
}
// componentDidUpdate() {
// this.scrollBottom()
// }
useEffect(() => {
scrollBottom()
})
const back = () => {
navigateTo(changeRoute, config.pages.home);
}
// const { userName } = this.state;
return (
<div className='app_container'>
<div className='no-comment'>
<button onClick={back} className="confirm_button">返回</button>
</div>
{renderList()}
<div className='input_style'>
<input
className='input_quertion'
type="text"
placeholder="请输入问题"
value={userName}
name="userName"
onChange={handleForm}
/>
<div style={{ width: '1%' }}></div>
<button onClick={addComment} className="confirm_button">发起提问</button>
</div>
</div>
)
}
const ClickFingerTextBoard = React.memo(({ dataList, index, updateScroll }) => {
console.log('组件' + index + "更新");
const [list, setList] = useState(dataList);
const [count, setCount] = useState(1);
let innerText = useRef([])
const delay = (time) => {
return new Promise((resolve) => {
let timers = setInterval(() => {
clearInterval(timers);
resolve();
}, time);
})
};
useEffect(() => {
let newList = [];
let timer1 = null;
let timer2 = null;
const calculatedFigures = async () => {
list.map((item) => {
newList.push(item.text.split(''));
})
timer1 = setTimeout(async () => {
for (let i = 0; i < newList.length; i++) {
innerText.current.push([]);
for (let j = 0; j <= newList[i].length; j++) {
if (newList[i][j] === undefined) {
continue;
}
await delay(Math.random() * 100);
innerText.current[i] = innerText.current[i] + newList[i][j];
updateScroll();
}
}
setTimeout(() => {
clearTimeout(timer1);
clearInterval(timer2);
}, 1000)
}, 0);
}
if (list && list.length) {
calculatedFigures();
timer2 = setInterval(() => {
setCount((count) => count + 1);
}, 100)
}
return () => {
clearTimeout(timer1);
clearInterval(timer2);
}
}, [])
return <div>{
innerText.current.length && innerText.current.map((text, index) => {
return <div style={{ marginLeft: 8, marginBottom: 10 }} key={index}>回答: <pre style={{ width: "100%" }}>{text}</pre></div>
})
}</div>
})
index.css
body,
html {
margin: 0;
}
ul,
li,
p {
padding: 0;
margin: 0;
list-style: none
}
h3 {
margin-bottom: 0;
}
.input_quertion {
width: 63%;
height: 50px;
border-radius: 10px;
border: 1px solid black;
padding-left: 20px;
}
.content {
width: 280px;
margin: 5px;
border: 1px solid black;
}
.no-comment {
text-align: center;
padding: 20px;
color: white;
background-color: rgb(53, 54, 65);
}
.app_container {
width: 100%;
height: 100%;
background-color: rgb(53, 54, 65);
}
.confirm_button {
width: 32%;
border-radius: 10px;
background-color: #03b96b;
border: 0;
height: 50px;
color: white;
}
.list_container {
overflow: auto;
max-height: calc(100vh - 100px);
}
.input_style {
width: 100%;
position: fixed;
display: flex;
bottom: 0;
padding: 1%;
}
.quiz {
display: flex;
height: 60px;
padding: 10px 40px 10px;
align-items: center;
color: white;
line-height: 41px;
background-color: rgb(53, 54, 65);
}
.quiz_avatar {
width: 30px;
height: 30px;
}
.answer {
display: flex;
background-color: #3b3d53;
color: white;
height: auto;
line-height: 35px;
padding: 20px 40px;
overflow: auto;
white-space: normal;
word-break: break-all;
}