yigal
yigal

Reputation: 4745

why is useRef not working in this example?

Here is my react hooks code:

function Simple(){
    var [st,set_st]=React.useState(0)
    var el=React.useRef(null)
    if (st<1)
        set_st(st+1)//to force an extra render for a grand total of 2
    console.log('el.current',el.current,'st',st)
    return <div ref={el}>simple</div>
}
ReactDOM.render(<Simple />,document.querySelector('#root') );

I thought it should render twice. the first time el.current should be null and second time is should point to the DOM object of the div. when running this, this was the output

el.current null st 0
el.current null st 1

It did, yes, render twice. however, the second render th el.current is still null. why?

solution: as described by Gireesh Kudipudi below. I added useEffect

function Simple(){
    var [st,set_st]=React.useState(0)
    var el=React.useRef(null)
    if (st<1)
        set_st(st+1)//to force an extra render for a grand total of 2
    console.log('el.current',el.current,'st',st)
    React.useEffect(_=>console.log('el.current',el.current,'st',st)) //this prints out the el.current correctly
    return <div ref={el}>simple</div>
}
ReactDOM.render(<Simple />,document.querySelector('#root') );

Upvotes: 2

Views: 22265

Answers (2)

Christopher Francisco
Christopher Francisco

Reputation: 16288

It could be that you are fixated on amount of renderings which is not necessarily the best React mindset when working with hooks. Mindset should be more like what has changed in my world.

From that point, try adding a useEffect and tell it you're interested in seeing when ref changes within my world. Try the following example, and see the behavior yourself.

let renderCounter = 0;

function Simple() {
  const [state, setState] = useState()
  const ref = React.useRef(null)

  if (state < 1) {
    /** 
     * We know this alter the state, so a re-render will happen
     */
    setState('foo')
  }

  useEffect(() => {
    /**
     * We don't know exactly when is `ref.current` going to
     * point to a DOM element. But we're interested in logging
     * when it happens.
     */
    if (ref.current) {
      console.log(ref.current)

      /**
       * Try commenting and uncommenting the next line, and see
       * the amount of renderings
       */
       setState('bar');
    }

  }, [ref]);

  renderCounter = renderCounter + 1
  console.log(renderCounter);

  return <div ref={el}>simple</div>
}

React will re-render when the ref has been initialized with a value, but that doesn't mean it will happen on the 2nd render.

To answer your question, you haven't told react what to do when ref changes.

Upvotes: 5

Gireesh
Gireesh

Reputation: 498

class SimpleComponent extends React.Component{
  el = React.createRef(null)
  constructor(props){
    super(props)
    this.state = {
      st:0
    }
  }
  componentDidMount(){
    if(this.state.st<1)
      this.setState(prevState=>{
        return {st:prevState.st+1}
      })
  }

  render(){
    console.log('el.current',this.el.current,'st',this.state.st)
    return <div ref={this.el}>simple</div>
  }
}

ReactDOM.render(<SimpleComponent />,document.querySelector('#root') );

Output is

el.current null st 0
el.current <div>​simple​</div>​ st 1

According to Documentation

ReactDOM.render(element, container[, callback])

Render a React element into the DOM in the supplied container and return a reference to the component (or returns null for stateless components).

Since you are trying to reference a functional component it might be reason.It is interesting scenario i came across because of your question.

Also if used as child component the output is as expected

Upvotes: 0

Related Questions