React中优化同级textarea组件高度同步的性能问题
问题描述
需求目标
- 让同一行内的所有
<textarea/>组件保持相同的height样式 - 当任意一个
<textarea/>的高度变化时,该行所有同组件同步更新高度
当前实现方案
父组件(状态提升)
通过将高度状态提升至行组件,实现该行所有子组件共享高度值:
const TableRow = (props: TableRowProps) => { const [textAreaHeight, setTextAreaHeight] = useState<string>("100%"); console.log("parent render"); return ( <tr> {props.currentRow.cells.map((rowValue) => ( <DynamicCellTextArea key={rowValue.id} currentRow={rowValue} height={textAreaHeight} setHeight={setTextAreaHeight} /> ))} </tr> ); };
子组件(MutationObserver监听)
使用MutationObserver监听textarea的style属性变化,更新共享状态以同步所有同组件高度:
const DynamicCellTextArea = (props: DynamicCellTextAreaProps) => { const textareaRef = useRef<HTMLTextAreaElement>(null); useEffect(() => { if (textareaRef.current === null) { return; } textareaRef.current.style.minWidth = "100%"; textareaRef.current.style.minHeight = "100%"; textareaRef.current.style.height = props.height; const mutationObserver = new MutationObserver(() => { props.setHeight(textareaRef.current?.style.height as string); }); mutationObserver.observe(textareaRef.current, { attributes: true, attributeFilter: ["style"], }); }, [props]); return ( <td className="h-px border border-red-400 overflow"> <div className="flex w-full h-full"> <textarea ref={textareaRef} rows={1} className="w-full px-3 py-2 bg-red-500 resize" /> </div> </td> ); }
现有问题
表格包含大量行列时,渲染卡顿明显。曾尝试用onResize/onResizeCapture事件捕获resize动作来更新状态,但未成功,寻求优化方案。
优化方案
1. 减少不必要的重渲染
父组件优化
用useCallback稳定状态更新函数的引用,避免每次渲染生成新函数导致子组件无意义重渲染:
const TableRow = (props: TableRowProps) => { const [textAreaHeight, setTextAreaHeight] = useState<string>("100%"); const handleHeightChange = useCallback((newHeight: string) => { setTextAreaHeight(newHeight); }, []); return ( <tr> {props.currentRow.cells.map((rowValue) => ( <DynamicCellTextArea key={rowValue.id} currentRow={rowValue} height={textAreaHeight} setHeight={handleHeightChange} /> ))} </tr> ); };
子组件优化
给子组件添加React.memo,仅当props真正变化时才重新渲染:
const DynamicCellTextArea = React.memo((props: DynamicCellTextAreaProps) => { // 原有子组件逻辑 });
2. 替换MutationObserver为ResizeObserver
MutationObserver会监听所有style属性变化,容易触发冗余回调。改用ResizeObserver可精准监听元素尺寸变化:
useEffect(() => { if (!textareaRef.current) return; const textarea = textareaRef.current; textarea.style.minWidth = "100%"; textarea.style.minHeight = "100%"; textarea.style.height = props.height; const resizeObserver = new ResizeObserver((entries) => { const { height } = entries[0].contentRect; // 仅当高度变化超过1px时更新,避免频繁触发 if (Math.abs(parseFloat(textarea.style.height) - height) > 1) { props.setHeight(`${height}px`); } }); resizeObserver.observe(textarea); return () => { resizeObserver.unobserve(textarea); }; }, [props.height, props.setHeight]);
3. 防抖处理高度更新
若ResizeObserver仍触发过于频繁,可添加防抖函数减少状态更新次数:
// 定义防抖工具函数 const debounce = (fn: Function, delay: number) => { let timeoutId: NodeJS.Timeout; return (...args: any[]) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn(...args), delay); }; }; // 在子组件effect中使用 useEffect(() => { if (!textareaRef.current) return; const textarea = textareaRef.current; textarea.style.minWidth = "100%"; textarea.style.minHeight = "100%"; textarea.style.height = props.height; const debouncedSetHeight = debounce((height: string) => { props.setHeight(height); }, 100); const resizeObserver = new ResizeObserver((entries) => { const { height } = entries[0].contentRect; debouncedSetHeight(`${height}px`); }); resizeObserver.observe(textarea); return () => { resizeObserver.unobserve(textarea); clearTimeout(debouncedSetHeight as any); }; }, [props.height, props.setHeight]);
4. 优化父组件渲染
若TableRow因其他props频繁触发渲染,可添加React.memo并自定义比较逻辑:
const TableRow = React.memo((props: TableRowProps) => { // 原有父组件逻辑 }, (prevProps, nextProps) => { // 仅当行ID变化时才重新渲染 return prevProps.currentRow.id === nextProps.currentRow.id; });
内容的提问来源于stack exchange,提问作者jd0x0021
相关产品推荐
相关产品推荐

