原生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 操作最标准的回答。

相关推荐
糖拌西瓜皮1 小时前
Node.js核心模块实战:文件、路径、HTTP与流处理
javascript·node.js
糖拌西瓜皮1 小时前
NestJS入门指南:Java开发者的Spring Boot体验
javascript·node.js
糖拌西瓜皮1 小时前
Express框架快速上手:中间件、路由与错误处理
javascript·node.js
禅思院3 小时前
前端部署“三层漏斗”完全指南:从CI/CD到自动回滚的工程化实战【开题】
前端·架构·前端框架
快乐肚皮4 小时前
深入理解Loop Engineering
前端·后端
半个落月4 小时前
从递归到快速排序:用 JavaScript 把分治思想讲明白
javascript·算法·面试
风骏时光牛马4 小时前
VHDL十大经典基础功能设计实例代码合集
前端
小兔崽子去哪了4 小时前
Vue3 + Pinia 集成 IGV.js 实现 BAM 文件在线浏览
javascript·vue.js·后端
hunterandroid4 小时前
Notification 通知:从基础到渠道适配
前端