在React项目开发中,我们经常会遇到需要用户输入多行文本的场景,默认的textarea组件高度固定,当输入内容超过设定高度时就会出现纵向滚动条,不仅影响页面美观,也会打断用户的输入流程。实现动态高度的文本输入框,能够让输入框根据输入内容的长度自动调整高度,给用户更流畅的输入体验。

实现核心原理
动态高度文本输入框的核心逻辑是监听textarea的输入事件,在每次内容变化时,先将其高度重置为自动,再获取内容的实际滚动高度,最后将高度设置为该滚动高度。这里需要用到scrollHeight属性,这个属性可以返回元素内容的总高度,包括因溢出而无法在屏幕上显示的部分。
基础实现步骤
1. 创建基础组件结构
首先创建一个受控的textarea组件,绑定value和onChange事件,用于获取和存储输入的内容。
import React, { useRef, useState, useEffect } from 'react';
const DynamicTextarea = () => {
const [value, setValue] = useState('');
const textareaRef = useRef(null);
// 处理输入变化
const handleChange = (e) => {
setValue(e.target.value);
};
return (
<div className="dynamic-textarea-wrap">
<textarea
ref={textareaRef}
value={value}
onChange={handleChange}
placeholder="请输入内容"
style={{
width: '100%',
minHeight: '40px',
padding: '8px 12px',
boxSizing: 'border-box',
resize: 'none',
overflow: 'hidden'
}}
/>
</div>
);
};
export default DynamicTextarea;
2. 添加高度调整逻辑
接下来需要实现高度自动调整的函数,在组件挂载和内容变化时调用该函数,确保高度始终匹配内容长度。
import React, { useRef, useState, useEffect, useCallback } from 'react';
const DynamicTextarea = () => {
const [value, setValue] = useState('');
const textareaRef = useRef(null);
// 调整高度的函数
const adjustHeight = useCallback(() => {
const textarea = textareaRef.current;
if (!textarea) return;
// 先重置高度为auto,避免之前的高度影响计算
textarea.style.height = 'auto';
// 设置高度为内容的滚动高度
textarea.style.height = `${textarea.scrollHeight}px`;
}, []);
// 处理输入变化
const handleChange = (e) => {
setValue(e.target.value);
};
// 内容变化时调整高度
useEffect(() => {
adjustHeight();
}, [value, adjustHeight]);
// 组件初次挂载时调整一次高度
useEffect(() => {
adjustHeight();
}, [adjustHeight]);
return (
<div className="dynamic-textarea-wrap">
<textarea
ref={textareaRef}
value={value}
onChange={handleChange}
placeholder="请输入内容"
style={{
width: '100%',
minHeight: '40px',
padding: '8px 12px',
boxSizing: 'border-box',
resize: 'none',
overflow: 'hidden'
}}
/>
</div>
);
};
export default DynamicTextarea;
进阶优化方案
1. 添加最大高度限制
如果希望输入框高度达到一定值后不再继续增高,而是出现滚动条,可以添加最大高度的限制,避免输入框过高影响页面布局。
import React, { useRef, useState, useEffect, useCallback } from 'react';
const DynamicTextarea = ({ maxHeight = 200 }) => {
const [value, setValue] = useState('');
const textareaRef = useRef(null);
const adjustHeight = useCallback(() => {
const textarea = textareaRef.current;
if (!textarea) return;
textarea.style.height = 'auto';
// 获取内容高度和最大高度中的较小值
const newHeight = Math.min(textarea.scrollHeight, maxHeight);
textarea.style.height = `${newHeight}px`;
// 如果内容高度超过最大高度,显示滚动条
textarea.style.overflowY = textarea.scrollHeight > maxHeight ? 'auto' : 'hidden';
}, [maxHeight]);
const handleChange = (e) => {
setValue(e.target.value);
};
useEffect(() => {
adjustHeight();
}, [value, adjustHeight]);
useEffect(() => {
adjustHeight();
}, [adjustHeight]);
return (
<div className="dynamic-textarea-wrap">
<textarea
ref={textareaRef}
value={value}
onChange={handleChange}
placeholder="请输入内容"
style={{
width: '100%',
minHeight: '40px',
padding: '8px 12px',
boxSizing: 'border-box',
resize: 'none',
overflow: 'hidden'
}}
/>
</div>
);
};
export default DynamicTextarea;
2. 封装为可复用组件
为了在项目多个地方使用,可以将其封装为通用组件,支持传入自定义样式、占位符、最大高度等属性。
import React, { useRef, useState, useEffect, useCallback } from 'react';
const DynamicTextarea = ({
value: propValue,
onChange,
placeholder = '请输入内容',
maxHeight,
style = {},
className = ''
}) => {
const [innerValue, setInnerValue] = useState(propValue || '');
const textareaRef = useRef(null);
// 处理受控和非受控模式
const currentValue = propValue !== undefined ? propValue : innerValue;
const adjustHeight = useCallback(() => {
const textarea = textareaRef.current;
if (!textarea) return;
textarea.style.height = 'auto';
if (maxHeight) {
const newHeight = Math.min(textarea.scrollHeight, maxHeight);
textarea.style.height = `${newHeight}px`;
textarea.style.overflowY = textarea.scrollHeight > maxHeight ? 'auto' : 'hidden';
} else {
textarea.style.height = `${textarea.scrollHeight}px`;
textarea.style.overflowY = 'hidden';
}
}, [maxHeight]);
const handleChange = (e) => {
const newValue = e.target.value;
if (propValue === undefined) {
setInnerValue(newValue);
}
onChange && onChange(newValue);
};
useEffect(() => {
adjustHeight();
}, [currentValue, adjustHeight]);
useEffect(() => {
adjustHeight();
}, [adjustHeight]);
return (
<textarea
ref={textareaRef}
value={currentValue}
onChange={handleChange}
placeholder={placeholder}
className={className}
style={{
width: '100%',
minHeight: '40px',
padding: '8px 12px',
boxSizing: 'border-box',
resize: 'none',
...style
}}
/>
);
};
export default DynamicTextarea;
常见问题说明
- 为什么需要先重置高度为auto?因为如果不重置,之前设置的高度会影响
scrollHeight的计算结果,导致高度调整不准确。 - 为什么要设置
resize: none?避免用户手动拖拽改变textarea的高度,干扰自动调整的逻辑。 - 为什么要用
useCallback包裹调整高度的函数?避免函数每次渲染都重新创建,导致不必要的useEffect执行。
使用示例
在页面中引入封装好的组件,传入对应的属性即可使用:
import React from 'react';
import DynamicTextarea from './DynamicTextarea';
const App = () => {
const [content, setContent] = React.useState('');
return (
<div style={{ width: '500px', margin: '50px auto' }}>
<h3>动态高度文本输入框示例</h3>
<DynamicTextarea
value={content}
onChange={setContent}
placeholder="请在这里输入多行内容,输入框会自动调整高度"
maxHeight={300}
/>
<p>当前输入内容长度:{content.length}</p>
</div>
);
};
export default App;
React动态高度文本输入框textareaauto_height前端组件修改时间:2026-06-15 00:36:42