React TypeScript类组件如何正确保存元素ref供resize事件使用
问题核心原因
你遇到resize事件触发时ref值为null,是两处错误写法导致的:
- ref类型定义错误:
React.LegacyRef<HTMLDivElement>是兼容旧版字符串ref、回调ref的联合类型,和React.createRef()返回的RefObject类型不匹配,导致DOM挂载阶段无法正确把元素实例赋值给ref的current属性。 - 事件绑定时机不合理:在constructor中绑定resize事件时,组件还未完成挂载,目标DOM节点还未渲染完成,不过这个问题会在组件挂载后自动消失,不是ref持续为null的核心原因。
正确实现代码
import React, { Component } from 'react'; // 按业务实际定义Props、State类型即可 interface CarouselProps {} interface CarouselState {} export default class Carousel extends Component<CarouselProps, CarouselState> { // 正确定义ref类型,直接初始化赋值 private carouselRef = React.createRef<HTMLDivElement>(); componentDidMount() { // DOM挂载完成后再绑定resize事件 window.addEventListener('resize', this.resizeCarouselElements); // 初始化时主动触发一次,拿到元素初始状态 this.resizeCarouselElements(); } componentWillUnmount() { // 组件卸载时移除事件监听,避免内存泄漏 window.removeEventListener('resize', this.resizeCarouselElements); } resizeCarouselElements = () => { // 增加非空判断,覆盖极端场景下ref未挂载的情况 if (this.carouselRef.current) { const targetElement = this.carouselRef.current; // 此处可正常读取目标元素的尺寸、位置等状态,例: // const elementWidth = targetElement.offsetWidth; // const elementHeight = targetElement.offsetHeight; } }; render() { return ( <div className='name' ref={this.carouselRef}> {/* 轮播业务内容 */} </div> ); } }
Ref存储最佳实践
- 禁止将ref存入组件state:ref的变动不会触发组件重渲染,存入state没有实际意义,反而会引发不必要的重渲染和类型异常。
- 类组件中直接用类的私有实例属性存储ref即可:这是官方推荐的标准写法,ref对象和组件实例生命周期一致,访问方便,不会被意外篡改。
- 不要在constructor、render执行阶段直接读取
ref.current:这两个阶段DOM还未完成挂载/更新,读取到的要么是null要么是过期的DOM实例,读取操作要放在componentDidMount、componentDidUpdate生命周期,或者事件回调、用户交互触发的方法中。 - 函数组件中使用
useRef存储DOM引用或者其他不需要触发重渲染的可变值,逻辑和类组件实例属性存ref完全一致,ref对象在组件全生命周期保持引用稳定。 - 除了DOM引用,定时器ID、上一轮props/state缓存值这类不需要触发重渲染的可变数据,都适合存在ref中,不要存入state。
内容的提问来源于stack exchange,提问作者EaredGen
相关产品推荐
相关产品推荐

