如何在React中使用map方法向textarea添加动态内容并实现自动滚动
问题根因
<textarea>的value属性仅支持纯字符串输入,你传入JSX元素数组时,React会自动调用对象的toString()方法,所以会输出[object Object]。同时textarea本身不支持渲染内部HTML标签、自定义样式类,你写的small、p标签都无法在textarea内生效。- 你当前的时间生成逻辑存在问题:放在组件顶层的时间会在每次组件重渲染时刷新所有消息的时间,无法保留每条消息实际接收的时间戳。
- 原代码中部分DOM属性写了
class,React规范需要用className,否则会出现属性不生效的警告。
解决方案
方案1:用可滚动div替代textarea(推荐,支持自定义样式)
这是最优解,既能保留你需要的消息样式,也能轻松实现自动滚动效果,修改后代码如下:
import { Fragment, useEffect, useRef } from "react"; import { format } from "date-fns"; const Chat = ({ name, message }) => { // 绑定聊天容器的ref用于控制滚动 const chatLogRef = useRef(null); // 监听消息数组变化,新消息进来时自动滚动到底部 useEffect(() => { if (chatLogRef.current) { chatLogRef.current.scrollTop = chatLogRef.current.scrollHeight; } }, [message]); return ( <Fragment> <div> <h3 className="d-inline-block me-3"> Chat log </h3> {name && ( <span className="me-3 d-inline-block"> <div className="spinner-grow spinner-grow-sm text-success" style={{ fontSize: "10px" }} role="status" > <span className="visually-hidden">Loading...</span> </div> </span> )} <small className="text-muted d-block"> {name}</small> {/* 模拟textarea样式的可滚动div */} <div ref={chatLogRef} style={{ width: "fit-content", height: "160px", border: "1px solid #ced4da", borderRadius: "0.25rem", padding: "0.375rem 0.75rem", overflowY: "auto", backgroundColor: "#fff" }} > {message.map((eachMsg, index) => { // 建议在socket接收消息时就把时间存入消息对象,避免重渲染刷新所有时间 // 这里用你引入的date-fns格式化时间,比手动拼接更简洁 const timeStr = format(new Date(), 'HH:mm:ss') return ( <Fragment key={index}> <small className="text-muted d-inline-block">{timeStr}</small> <p className="d-block shadow p-1 fw-bold rounded text-success" style={{ fontFamily: "cursive" }} > {eachMsg} </p> </Fragment> ); })} </div> </div> </Fragment> ); }; export default Chat;
方案2:必须使用textarea的处理方式
如果业务要求必须用textarea,就需要放弃自定义样式,把所有内容拼接为纯字符串传入value:
<textarea ref={chatLogRef} cols="70" rows="8" value={message.map(eachMsg => { const timeStr = format(new Date(), 'HH:mm:ss') return `${timeStr}\n${eachMsg}\n` }).join('')} ></textarea>
自动滚动逻辑和方案1一致,给textarea绑定ref后在useEffect中控制滚动位置即可。
优化建议
建议你在接收到socket消息存入数组时,就把当前时间一起存到消息结构中,比如每条消息格式为{ content: '消息内容', time: new Date() },这样历史消息的时间不会随着组件重渲染被覆盖。
内容的提问来源于stack exchange,提问作者nodeDev
相关产品推荐
相关产品推荐

