React 的 Textarea 高度自适应
上次提到过我接触了一个新项目,是校友们策划的一个类 ChatGPT 的项目,我负责前端部分,用的是 React + TailwindCSS 的组合。 我这个刚接触 React 一个月的小白肯定是搓手等着上手、跃跃欲试。
像是 ChatGPT、Claude,甚至是 Discord 这样的聊天室 App,输入框都是能够让用户换行、输入代码块的。我们的项目也不例外。 但是,textarea 组件就算是默认单行,换行时也会向下增加高度,导致脱离原本的父容器,甚至跑到屏幕外面去。
最终结果

环境
先说父容器的样式,一个带了 flex 的 div:
<div className="flex flex-col h-full"> <div className="flex-1">{/* 信息内容 */}</div> {/* 主角 */} <Textbox /></div>而 Textbox 组件的样式差不多是这样的:
<div className="flex items-center w-full"> <textarea name="message" className="w-full resize-none" placeholder="Type a message..." rows="1" /> <button>发送</button></div>正常来说,textarea 的大小是朝下无限增长的,但这不是我们想要的,我们希望它能够朝上增长、挤压信息内容的高度,直到达到一定高度后出现滚动条。
解决方案
为什么要特意说到父容器是一个带了 flex 的 div 呢?因为这是解决问题的关键。
包含了信息内容的兄弟元素会铺满剩余没有被 Textbox 组件占用的空间,而 Textbox 组件的高度是可以动态修改的。
也就是说,我们可以通过监听 textarea 的 scrollHeight 属性,来动态修改 Textbox 整个组件的高度,最终保持让它一直待在父容器的里面。
先创建一个 useRef 来引用 textarea:
const textareaRef = useRef(null);// ... <textarea ref={textareaRef} />我们还需要创建一个 useState 来保存我们想要的高度:
const [height, setHeight] = useState(40);这里的
40(像素)是我自己设定的一个最小高度。
创建 useEffect 来监听 textarea 的 scrollHeight 属性:
useEffect(() => { const handleResize = () => { const textareaElement = textareaRef.current; textareaElement.style.height = "auto"; textareaElement.style.height = `${textareaElement.scrollHeight}px`; setHeight(Math.max(40, textareaElement.scrollHeight)); } const textareaElement = textareaRef.current; textareaElement.addEventListener("input", handleResize); return () => textareaElement.removeEventListener("input", handleResize);}, []);在 handleResize 中,我们先将 textarea 的高度设置为 auto,这样就可以让它自己决定高度,然后用 setHeight 来保存 scrollHeight 的值。
Math.max(40, textareaElement.scrollHeight)是为了保证textarea的高度不会小于40像素,也就是我刚才说的,我自己设定的一个最小高度。
handleResize 需要在用户输入时触发,所以还要用 addEventListener 来监听 input 事件。
最后别忘了卸载监听器。
那么我们得到的 height 要用在哪里呢?Textbox 组件的最外层 div 上:
<div className="flex items-center w-full" style={{ height: `${height}px` }}> <textarea name="message" className="w-full resize-none" placeholder="Type a message..." rows="1" ref={textareaRef} /> <button>发送</button></div>每次用户输入,height 都会被更新,Textbox 组件的高度也会被更新。拥有着固定高度的 Textbox 组件能够在 Flexbox...
剩余内容已隐藏