After reading forwarding refs from react legacy docs and following the examples from the SO post on using ref forwarding on classes, I tried to create a working example as following:
class Card extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
//after three seconds focus myRef
setTimeout(() => {
console.log(this.myRef);
this.myRef.current.focus();
}, 3000);
}
render() {
return <FancybtnWthRef ref={this.myRef} txt="random"/>;
}
}
//Fancybtn class based componenet with ref forwarding
class Fancybtn extends React.Component {
constructor(props) {
super(props);
const { innerRef, ...props2 } = this.props;
console.log(innerRef, this.props.innerRef); //doesn't get live binding to 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')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
On the other hand, if I grab the this.props.innerRef
object directly for button, then it works:
class Card extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
//after three seconds focus myRef
setTimeout(() => {
console.log(this.myRef);
this.myRef.current.focus();
}, 3000);
}
render() {
return <FancybtnWthRef ref={this.myRef} txt="random"/>;
}
}
//Fancybtn class based componenet with ref forwarding
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')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
In plain javascript destructuring gets live binding of objects as following:
var obj = {x:1, obj2: {y: 33}}
const {x, obj2} = obj;
obj2.y = 44;
cconsole.log(obj.obj2.y); //gives 44
Using the same argument why doesn't const { innerRef, ...props } = this.props;
grab the actual this.props.innerRef
for the button
?
const { innerRef, ...props2 } = this.props;
does correctly get the innerRef
property from props
. However, this creates two local variables. It does not set the actual innerRef
property on the instance itself. Thus, this.innerRef
is undefined
and passing that as the ref to the <button>
has no effect. Note that unlike other languages like Java, accessing a variable without using this
will not attempt to look up that variable in the fields of the enclosing class instance.
You would have to manually assign the innerRef
to the instance to see it work:
this.innerRef = innerRef;
Or, to destructure the properties onto this
, you can rename the property like so:
({ innerRef: this.innerRef, ...this.props2 } = this.props);