原生DOM操作在React 中的注意事项

在 React 中,可以使用原生 DOM 操作,但要谨慎使用 。因为 React 本身通过 Virtual DOM(虚拟DOM) 来管理页面,如果直接操作真实 DOM,可能会与 React 的状态管理机制产生冲突。


一、为什么 React 不推荐直接操作 DOM

传统 JavaScript:

ini 复制代码
const div = document.getElementById('box');
div.innerHTML = 'Hello';
div.style.color = 'red';

React:

javascript 复制代码
function App() {
  const [text, setText] = useState('Hello');

  return (
    <div>
      <div>{text}</div>
      <button onClick={() => setText('World')}>
        修改
      </button>
    </div>
  );
}

React 的核心思想:

sql 复制代码
State
 ↓
Virtual DOM
 ↓
Real DOM

如果你直接修改:

ini 复制代码
document.getElementById('box').innerHTML = '123';

React 下一次渲染时:

scss 复制代码
setText('World');

React 会重新生成 DOM。

结果:

复制代码
你的修改可能被覆盖

二、React中操作DOM的正确方式

1. 使用 useRef(最推荐)

获取DOM

javascript 复制代码
import { useRef } from 'react';

function App() {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        聚焦
      </button>
    </>
  );
}

相当于

原生:

dart 复制代码
document.querySelector('input');

React:

复制代码
inputRef.current

2. 获取元素尺寸

开发中经常遇到:

复制代码
获取宽高
获取位置
滚动距离

offsetWidth

ini 复制代码
const divRef = useRef();

useEffect(() => {
  console.log(divRef.current.offsetWidth);
}, []);
xml 复制代码
<div ref={divRef}>内容</div>

getBoundingClientRect

arduino 复制代码
useEffect(() => {
  const rect = divRef.current.getBoundingClientRect();

  console.log(rect.width);
  console.log(rect.height);
  console.log(rect.top);
  console.log(rect.left);
}, []);

三、React中监听DOM事件

错误写法

scss 复制代码
useEffect(() => {
  document
    .getElementById('btn')
    .addEventListener('click', fn);
}, []);

容易造成:

复制代码
事件重复绑定
内存泄漏

正确写法

React事件系统:

xml 复制代码
<button onClick={handleClick}>
  点击
</button>

如果必须监听

例如:

javascript 复制代码
window
document
scroll
resize

使用:

javascript 复制代码
useEffect(() => {
  const fn = () => {
    console.log('resize');
  };

  window.addEventListener('resize', fn);

  return () => {
    window.removeEventListener('resize', fn);
  };
}, []);

四、操作样式注意事项

错误

ini 复制代码
divRef.current.style.color = 'red';

React重新渲染:

scss 复制代码
setState(...)

可能被覆盖。


推荐

State控制

javascript 复制代码
const [color, setColor] = useState('red');

return (
  <div style={{ color }}>
    hello
  </div>
);

Class控制

css 复制代码
<div className={isActive ? 'active' : ''}>
  内容
</div>

五、操作表单注意事项

错误

ini 复制代码
input.value = '张三';

React推荐

ini 复制代码
const [name, setName] = useState('');

<input
  value={name}
  onChange={(e) => setName(e.target.value)}
/>

这叫:

复制代码
受控组件(Controlled Component)

六、操作滚动条

这是 React 中最常见的 DOM 操作之一。

滚动到顶部

css 复制代码
window.scrollTo({
  top: 0,
  behavior: 'smooth',
});

指定元素滚动

ini 复制代码
divRef.current.scrollTop = 100;

滚动到某个元素

css 复制代码
divRef.current.scrollIntoView({
  behavior: 'smooth',
});

七、React StrictMode 注意事项

React18 开发模式下:

xml 复制代码
<React.StrictMode>
  <App />
</React.StrictMode>

会导致:

scss 复制代码
useEffect(() => {
  console.log('执行');
}, []);

输出:

复制代码
执行
执行

原因:

复制代码
开发模式故意执行两次
检测副作用

因此 DOM 操作要避免:

复制代码
创建两次
绑定两次
请求两次

例如:

scss 复制代码
useEffect(() => {
  const timer = setInterval(() => {}, 1000);

  return () => {
    clearInterval(timer);
  };
}, []);

一定要写清理函数。


八、React中绝对不要这样做

1. 直接修改React管理的DOM

ini 复制代码
document.getElementById('root').innerHTML =
  '<h1>Hello</h1>';

React会失控。


2. 使用jQuery操作React节点

javascript 复制代码
$('#box').hide();

React重新渲染后:

复制代码
状态不同步

3. 在 render 中操作DOM

错误:

javascript 复制代码
function App() {
  document.title = '首页';

  return <div>hello</div>;
}

应该:

ini 复制代码
useEffect(() => {
  document.title = '首页';
}, []);

九、React中必须操作DOM的场景

以下场景允许且常见:

场景 方法
输入框聚焦 ref.focus()
获取宽高 offsetWidth
获取位置 getBoundingClientRect
滚动控制 scrollTop
Canvas绘图 canvasRef
视频播放 video.play()
音频播放 audio.play()
第三方库集成 ECharts、Mapbox、Three.js
文件上传 inputRef.current.click()
拖拽功能 drag/drop API
IntersectionObserver ref + observer

十、前端面试高频回答

如果面试官问:

React 中为什么不推荐直接操作 DOM?

可以回答:

React 采用 Virtual DOM 和单向数据流机制,页面应由 State 驱动。如果直接操作真实 DOM,会绕过 React 的更新流程,导致状态与视图不一致,甚至被 React 的重新渲染覆盖。因此 React 推荐通过 state、props 控制 UI,仅在获取节点、聚焦、滚动、获取尺寸、集成第三方库等场景下,通过 useRef 和 useEffect 操作 DOM。

这是中高级 React 开发面试中关于 DOM 操作最标准的回答。

相关推荐
CarIise28 分钟前
下拉菜单HTML/CSS/JS实现
前端·css
2601_9620715738 分钟前
Java进阶(vue基础)
前端·javascript·vue.js
研☆香38 分钟前
数组方法 splice讲解 拓展
开发语言·前端·javascript
码视野39 分钟前
基于 Spring Boot + Vue3 的【城市地下燃气管网微泄漏感知与相邻地下空间燃爆预警中台】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端
淡海水42 分钟前
05-03-栈队列-PriorityQueue-TElement-TPriority-NET6优先队列语义与四叉堆实现
服务器·前端·c#·priorityqueue·clr·telement
————A1 小时前
Agent 文件处理中间件
开发语言·javascript·ecmascript
ITresearchGuest1 小时前
LangChain JS 入门:快速搭建前端 AI 开发环境
前端·javascript·langchain
YHHLAI1 小时前
React + TypeScript 实战:从零构建颜色选择器应用
前端·react.js·typescript
小磊哥er1 小时前
深入解构Claude Code - 第 1 篇 · 先认识它
javascript·ai编程
Profile排查笔记2 小时前
指纹浏览器手机版怎么选?从本地 App 到云端 Android 的实现方式解析
前端·人工智能·后端·自动化