Sharcoux
Sharcoux

Reputation: 6085

current is always null when using React.createRef

I was trying to do this.

I must be missing something, but I don't understand why current is always null in this example.

class App extends React.PureComponent {
  constructor(props) {
    super(props);
    this.test = React.createRef();
  }
  render() {
    return <div className="App">current value : {this.test.current + ""}</div>;
  }
}

You can check my test case here

Upvotes: 54

Views: 104234

Answers (8)

ammar khaled
ammar khaled

Reputation: 39

In version 17.0.2 of React, refs and it being asynchronous got changed. Code like this stopped working properly after the update:

import {useRef} from 'react';
import './kind.css'

const Kind = ({prop}) => {

    // defining the ref
    const kindRef = useRef('')
    
    // print the ref to the console
    console.log(kindRef)


    return (
    <div ref={kindRef} className="kind-container" >
        <div className="kind" data-kind='drink'>Drink</div>
        <div className="kind" data-kind='sweet'>Sweet</div>
        <div className="kind" data-kind='lorem'>lorem</div>
        <div className="kind" data-kind='ipsum'>ipsum</div>
        <div className="kind" data-kind='ipsum' >food</div>
    </div>
    );
}

export default Kind;

After initializing the ref it takes sometime to assign it to the dom. Javascript, being a synchronous language, doesn't wait for the ref to initialize and skips straight to the log.

To fix this we will need to use useEffect like this

import { useRef, useEffect} from 'react';
import './kind.css'

const Kind = ({prop}) => {

    // defining the ref
    const kindRef = useRef('')
    
    // using 'useEffect' will help us to do what ever you want to your ref varaible,
    // by waiting to letting the elements to mount:'mount means after the elements get inserted in the page' and then do what you want the ref
    useEffect(()=>{

        // print the ref to the console
        console.log(kindRef)        

    })


    return (
    <div ref={kindRef} className="kind-container" >
        <div className="kind" data-kind='drink'>Drink</div>
        <div className="kind" data-kind='sweet'>Sweet</div>
        <div className="kind" data-kind='lorem'>lorem</div>
        <div className="kind" data-kind='ipsum'>ipsum</div>
        <div className="kind" data-kind='ipsum' >food</div>
    </div>
    );
}

export default Kind;

useEffect waits to the ref to be assigned to the DOM element and then runs the function assigned to it.

Upvotes: 3

MikeL
MikeL

Reputation: 2894

If you are using the ref in useCallback (or another hook), remember to add the ref to the dependencies:

const doSomething = useCallback(() => {
 ref.current?.doIt();
}, [ref]);

Upvotes: 1

Neeraj Sewani
Neeraj Sewani

Reputation: 4287

This may happen in the following circumstances:

  • You have forgotten to pass your ref to the component i.e this.test from the question.

<Component ref={this.test} />

  • You are using Redux in which the component to which ref props is passed is wrapped by connect method and hence this.test.current would return null as it points to the Redux wrapper, to make this kind of component work make forwardRef: true

i.e: connect(mapStateToProps, mapDispatchToProps, null, {forwardRef: true})(Component)

  • If you are using withRouter and connect together then instead of one here you are having two wrappers and this.test.current would obviously return null, to overcome this make sure withRouter wraps connect

withRouter(connect(mapStateToProps, mapDispatchToProps, null, {forwardRef: true})(Component))

and then

<Component wrappedComponentRef={ref => this.test = ref} />

wrappedComponentRef is a prop used to make the wrapped component available just as forwardRef: true, you can find it in docs here

Upvotes: 9

93_virus
93_virus

Reputation: 1

<div ref={this.test}>

You have to assign ref prop to your DOM element. In this case you have to assign ref to div element

Upvotes: -2

sktguha
sktguha

Reputation: 564

I know this is not the solution to OP's problem but for those who are coming from google search, one of the ways the ref.current can be null is that if the component on which the ref is being attached is a connected component. Like with react-redux connect or withRouter. For react-redux the solution is to pass forwardRef:true in the fourth option to connect.

Upvotes: 18

Mayank Shukla
Mayank Shukla

Reputation: 104379

Because you forgot to assign the ref to some dom element. You are only creating it.

Write it like this:

class App extends React.PureComponent {
  constructor(props) {
    super(props);
    this.test = React.createRef();
  }

  handleClick = () => alert(this.test.current.value)

  render() {
    return (
      <div className="App">
        <input ref={this.test} />
        <button onClick={this.handleClick}>Get Value</button>
      </div>
    )
  }
}

Working Example.

Upvotes: 52

Rex Raphael
Rex Raphael

Reputation: 200

React.createRef() is asynchronous so if you try to access the ref in componentDidMount, it will return null and later return the properties of the component in which you are referencing.

componentDidMount(): void {
      if (this.viewShot && this.viewShot.current) {
          this.viewShot.current.capture().then((uri) => {
        console.log('do something with ', uri);
          });
      }
  }

This is the right way to use the React.createRef() in this context.

Upvotes: 13

AKX
AKX

Reputation: 168913

You're missing the ref={this.test} prop.

return (
  <div className="App" ref={this.test}>
    current value : {this.test.current + ""}
  </div>
);

Upvotes: 8

Related Questions