vietvoquoc
vietvoquoc

Reputation: 833

Why ref object existed before assign it to target in Reactjs

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 enter image description here

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

Answers (2)

Drew Reese
Drew Reese

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:

enter image description here

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}

enter image description here

Edit zealous-jackson-6gvo0

Upvotes: 1

Robert
Robert

Reputation: 2753

Because console keep reference to current value to object (so you get value of object after rendering). If you will change your code to

console.log(JSON.stringif(ref))

Then you will get this:

enter image description here]1

Upvotes: 2

Related Questions