React中如何确保指定代码在DOM更新完成后执行?
解决方法
在React中,状态更新(比如setContent)是异步的,直接在setContent后操作DOM时,textarea的value还未同步更新,必须等DOM更新完成后再执行目标操作。以下是两种可行的实现方式:
方式一:通过useEffect监听content变化
useEffect会在DOM更新完成后触发,我们可以监听content的变化,在回调中执行需要的DOM操作:
import { useCallback, useEffect, useRef, useState } from 'react'; const YourComponent = ({ id }) => { const [content, setContent] = useState(''); const textAreaRef = useRef(null); // 用ref标记是否是粗体按钮触发的更新,避免其他操作触发不必要的DOM操作 const isBoldTriggered = useRef(false); const handleBoldClick = useCallback(() => { const selectionStart = textAreaRef.current?.selectionStart; const selectionEnd = textAreaRef.current?.selectionEnd; isBoldTriggered.current = true; if (selectionStart !== undefined && selectionEnd !== undefined) { setContent(prev => prev.substring(0, selectionStart) + '**' + prev.substring(selectionStart, selectionEnd) + '**' + prev.substring(selectionEnd) ); } else { setContent(prev => prev + '****'); } }, []); // 依赖项为空,因为使用了函数式更新获取最新content useEffect(() => { if (!isBoldTriggered.current) return; const textArea = textAreaRef.current; if (!textArea) return; // 处理无选中内容时的光标定位 if (content.endsWith('****')) { const cursorPos = content.length - 2; textArea.focus(); textArea.setSelectionRange(cursorPos, cursorPos); } // 触发change事件 const changeEvent = new Event('change', { bubbles: true }); textArea.dispatchEvent(changeEvent); // 重置标记 isBoldTriggered.current = false; }, [content]); const handleTextAreaChange = (e) => { setContent(e.target.value); }; return ( <textarea onChange={handleTextAreaChange} ref={textAreaRef} id={id} value={content} /> ); };
方式二:利用setContent的回调函数
setState的第二个参数是回调函数,它会在状态更新完成且DOM同步后执行,适合单个状态更新后的操作:
const handleBoldClick = useCallback(() => { const selectionStart = textAreaRef.current?.selectionStart; const selectionEnd = textAreaRef.current?.selectionEnd; if (selectionStart !== undefined && selectionEnd !== undefined) { setContent(prev => prev.substring(0, selectionStart) + '**' + prev.substring(selectionStart, selectionEnd) + '**' + prev.substring(selectionEnd) ); } else { setContent(prev => prev + '****', () => { const textArea = textAreaRef.current; if (!textArea) return; // 光标定位到添加的****中间 const cursorPos = textArea.value.length - 2; textArea.focus(); textArea.setSelectionRange(cursorPos, cursorPos); // 触发change事件 const changeEvent = new Event('change', { bubbles: true }); textArea.dispatchEvent(changeEvent); }); } }, []);
注意事项
- 不要在
handleBoldClick中直接使用content计算光标位置,此时content是旧值,需通过textArea.value.length或useEffect中的最新content获取正确长度; - 第一种方式更通用,适合处理多种状态更新后的DOM操作;第二种方式更直接,适合单一操作场景。
内容的提问来源于stack exchange,提问作者Anthony Bias
相关产品推荐
相关产品推荐

