使用antd的Input.TextArea,配置maxRows
和minRows
即可实现让输入框中的string自动伸缩展示。
例子:
// 最少展示2行,最多展示10行
<Input.TextArea autoSize={{ minRows: 2, maxRows: 10 }} />
但是它内部是怎么实现的呢?
实现
- step1:实现自适应高度
我们的目标是让 textarea 根据输入的内容自动调整高度。
为了实现这一功能,核心: 动态调整style.height
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`
- step2:防抖
使用Lodash的防抖功能,避免每次输入都导致不必要的渲染和高度调整
import { debounce } from 'lodash'; // 使用 Lodash 的 debounce
const AutoSizeTextarea = ({ onChange, maxLines = 10, rowHeight = 28, ...props }) => {
const [text, setText] = useState();
const textareaRef = useRef(null);
// 自适应高度的调整函数
const adjustHeight = useCallback(() => {
const textarea = textareaRef.current;
if (!textarea) return;
// 动态计算最大高度:行数 * 每行高度
const maxHeight = rowHeight * maxLines;
const currentHeight = textarea.scrollHeight;
// 只在需要时调整
if (textarea.style.height === `${currentHeight}px`) return;
// 防止超出最大高度
const newHeight = Math.min(currentHeight, maxHeight);
// 使用 requestAnimationFrame 来优化性能
requestAnimationFrame(() => {
textarea.style.height = 'auto'; // 先设置为 auto,避免高度被固定
textarea.style.height = `${newHeight}px`; // 设置为计算出的高度
});
}, [maxLines, rowHeight]);
// 使用 Lodash 的 debounce 来包装 adjustHeight,防抖延迟 300ms
const debouncedAdjustHeight = useCallback(debounce(adjustHeight, 300), [adjustHeight]);
// 初始化时调整高度
useEffect(() => {
adjustHeight(); // 只需要调用一次,不再使用防抖
}, [defaultValue]);
// 处理输入变化
const handleChange = (e) => {
const value = e.target.value;
setText(value);
onChange && onChange(e); // 保持原有 onChange 逻辑
// 调整高度
debouncedAdjustHeight();
};
return (
<textarea
value={text}
ref={textareaRef}
rows="1"
onChange={handleChange}
style={{ minHeight: '50px' }}
{...props}
/>
);
};
export default AutoSizeTextarea;