React Ref转发中,解构this.props为何无法获取innerRef引用?
类组件中Ref转发的解构绑定问题
我在阅读React Legacy文档的Ref转发内容,参考相关类组件Ref转发示例后,写了以下代码:
无法正常工作的代码示例
class Card extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); // 3秒后让myRef指向的元素获取焦点 setTimeout(() => { console.log(this.myRef); this.myRef.current.focus(); }, 3000); } render() { return <FancybtnWthRef ref={this.myRef} txt="random"/>; } } // 带Ref转发的类组件Fancybtn class Fancybtn extends React.Component { constructor(props) { super(props); const { innerRef, ...props2 } = this.props; console.log(innerRef, this.props.innerRef); // 无法获取this.props.innerRef的实时绑定 } render() { return ( <button className="fancy-btn" ref={this.innerRef} {...this.props2}> {this.props.txt} </button> ); } } const FancybtnWthRef = React.forwardRef((props, ref) => { return <Fancybtn {...props} innerRef={ref} />; }); ReactDOM.render( <Card />, document.getElementById('root') );
这段代码无法获取this.props.innerRef的实时绑定,但如果直接在button上使用this.props.innerRef,就能正常工作:
正常工作的代码示例
class Card extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); // 3秒后让myRef指向的元素获取焦点 setTimeout(() => { console.log(this.myRef); this.myRef.current.focus(); }, 3000); } render() { return <FancybtnWthRef ref={this.myRef} txt="random"/>; } } // 带Ref转发的类组件Fancybtn class Fancybtn extends React.Component { constructor(props) { super(props); const { innerRef, ...props2 } = this.props; console.log(innerRef, this.props.innerRef); } render() { return ( <button className="fancy-btn" ref={this.props.innerRef} {...this.props2}> {this.props.txt} </button> ); } } const FancybtnWthRef = React.forwardRef((props, ref) => { return <Fancybtn {...props} innerRef={ref} />; }); ReactDOM.render( <Card />, document.getElementById('root') );
在普通JavaScript里,对象解构是有实时绑定的,比如:
普通JS解构的对比示例
var obj = {x:1, obj2: {y: 33}} const {x, obj2} = obj; obj2.y = 44; console.log(obj.obj2.y); // 输出44
按照同样的逻辑,为什么在React中const { innerRef, ...props } = this.props;无法让button获取到有效的this.props.innerRef引用?
回答
问题核心出在类组件构造函数的执行时机和Ref的赋值逻辑上:
- 类组件的
constructor仅在初始化时执行一次,此时解构this.props.innerRef拿到的是初始状态的Ref对象,但这个Ref的current属性还未被React赋值为DOM元素——React会在组件挂载完成后,才会把DOM元素赋值给Ref的current。 - 你在
constructor里解构出innerRef后,并没有把它挂载到组件实例(this)上,所以render里的this.innerRef本质是undefined,自然无法绑定到button上。而直接使用this.props.innerRef时,render阶段引用的是实时的props对象,当React完成挂载赋值Ref的current后,这个引用能直接生效。
另外要明确:普通JS里的解构实时绑定,是因为解构的是对象引用,但这里的问题并非解构本身的绑定失效,而是你没有正确保存解构出的Ref引用,且构造函数执行时Ref的current还未完成赋值。
如果要让第一种写法生效,需要在constructor里把解构出的innerRef挂载到实例上:
class Fancybtn extends React.Component { constructor(props) { super(props); this.innerRef = props.innerRef; // 将props中的innerRef赋值给实例属性 const { innerRef, ...props2 } = this.props; this.props2 = props2; // 保存剩余props到实例 } render() { return ( <button className="fancy-btn" ref={this.innerRef} {...this.props2}> {this.props.txt} </button> ); } }
不过更简洁合理的做法,还是直接在render中使用this.props.innerRef,避免多余的实例属性赋值操作。
内容的提问来源于stack exchange,提问作者user31782
相关产品推荐
相关产品推荐

