You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:15:25