Reputation: 833
I tried Ref and forwarding-refs
in Reactjs to keep a reference to a DOM or React Component. I did not understand why the ref object
in which created kept correct target reference before target component was rendered or mounted.
I have the codesandbox right here to present my question more details. Here is the screenshot
As what you see, Ref object keep a correct reference to the target (in this case is FancyButton Component) even the render and ComponentDidMount method of target have not yet fired. Could someone possibly help me to understand about this more. Thanks.
Upvotes: 1
Views: 313
Reputation: 202801
You are console logging in quite a few wrong places, namely the console.log("render of Fancybutton");
in the render method of FancyButton
and in the function body of forwardRef
of the FancyHOC
. The render method should be a pure function without side-effect. Console logging is considered a side-effect.
Study this react component lifecycle diagram:
Notice where the render function resides. It resides in the "Render Phase" of the render cycle. Notice also that it "May be paused, aborted or restarted by React." This means they are called before anything in the "Commit Phase".
Notice now as well that the other component lifecycle methods, specifically componentDidMount
and its functional component coutnerpart useEffect
with empty dependency array are all called after the component has rendered (committed to DOM) at least once.
Fix the logging in the incorrect places:
FancyButtonHOC
Move the logs into componentDidUpdate
and useEffect
hook.
function createFancyButtonHOC(WrappedComponent) {
class FancyHOC extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return <WrappedComponent ref={forwardRef} {...rest} />;
}
componentDidMount() {
console.log("FancyHOC mounted");
}
componentDidUpdate() {
console.log("render of HOC: ", this.props.forwardRef);
}
}
return React.forwardRef((props, ref) => {
React.useEffect(() => {
console.log("forwardRef callback: ", ref);
});
return <FancyHOC forwardRef={ref} {...props} />;
});
}
In FancyButton
if you leave the console log in the render method it will simply log any time react is invoking the render method for DOM diffing purposes, not actually when it is rendered to the DOM during the commit phase. Move it to componentDidUpdate
.
export default class FancyButton extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
console.log("fancy button mounted");
}
componentDidUpdate() {
console.log("render of Fancybutton");
}
handleClick() {
console.log("button click");
}
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
New console log output
render of HOC: {current: FancyButton}
fancy button mounted
FancyHOC mounted
forwardRef callback: {current: FancyButton}
Upvotes: 1