类组件使用forwardRef时回调Ref函数仅触发一次的问题排查与解决求助
问题原因分析
你遇到的回调ref仅触发一次的核心问题在于组件的constructor只会在初始化阶段执行一次:
- 在子组件的
constructor里,你把props.forwardRef赋值给了实例属性this.ElemComponentRef,这个操作仅在组件第一次挂载时完成。 - 当父组件更新(比如点击按钮修改
value状态),子组件会重新渲染,但constructor不会再次执行,this.ElemComponentRef仍然是第一次初始化时的那个ref值。 - 如果父组件的ref是回调函数,每次父组件渲染都会生成新的函数实例,但子组件的div仍然绑定着旧的回调函数,导致新的回调无法触发;即便回调是同一个函数,也因为div的ref没有绑定到最新的
props.forwardRef,无法响应元素变化。
你直接用<div ref={this.props.forwardRef}>能解决问题,是因为每次render都会直接使用最新的props.forwardRef,但这样就失去了组件内部持有ref的能力。
修复方案
我们可以实现一个同时兼容外部传入ref(回调/对象式)和内部ref的逻辑,确保每次组件渲染时都能正确绑定ref。修改后的子组件代码如下:
class ElemComponent extends Component { constructor(props) { super(props); this.state = { value: '' }; // 创建组件内部专属的ref,用于自身业务逻辑 this.internalRef = React.createRef(); } // 示例:组件内部使用ref的方法 logInternalElement() { if (this.internalRef.current) { console.log('内部ref获取到的元素:', this.internalRef.current); } } render() { const { forwardRef } = this.props; return ( <div ref={(el) => { // 先将元素赋值给内部ref this.internalRef.current = el; // 再处理外部传入的ref if (typeof forwardRef === 'function') { // 回调式ref:直接调用函数传递元素 forwardRef(el); } else if (forwardRef) { // 对象式ref(React.createRef/useRef创建的):赋值给current属性 forwardRef.current = el; } }} > Div has a ref </div> ) } } export default React.forwardRef((props, ref) => <ElemComponent forwardRef={ref} {...props} />);
方案说明
- 保留内部ref:通过
this.internalRef = React.createRef()创建组件自己的ref,后续可以在组件内部的任意方法中通过this.internalRef.current访问DOM元素。 - 动态响应外部ref:使用回调ref作为div的ref属性,每次组件渲染时这个回调都会执行:
- 先把DOM元素同步到内部ref;
- 再根据外部ref的类型做对应处理,确保外部ref能实时获取到最新的DOM元素。
- 兼容所有ref类型:不管父组件传入的是回调式ref,还是
createRef/useRef创建的对象式ref,这个逻辑都能正确处理。
修改后,父组件的回调ref会在组件挂载、更新、卸载时正常触发,同时子组件内部也保留了独立的ref对象,完全满足你的需求。
内容的提问来源于stack exchange,提问作者Arjun Vinod
相关产品推荐
相关产品推荐

