52

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

Sharcoux
  • 5,546
  • 7
  • 45
  • 78

8 Answers8

50

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.

Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
  • 4
    I realize this was answered a while ago, but the working example you provided says "current value : null". Shouldn't it give a value? – Darin Cardin Feb 13 '20 at 22:15
  • Why do we need to assign the ref in the constructor? In my case i did not have a constructor. initialized the ref, however it was coming back as null. – Artexias Dec 21 '22 at 22:02
17

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.

sktguha
  • 524
  • 3
  • 21
13

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.

Rex Raphael
  • 200
  • 2
  • 7
  • 1
    You're totally wrong! React updates all DOM and refs before componentDidMount fires. What is capture method? It doesn't exist both for 'current' element as ref instance and nor for input element I just can't figure out why there's so much positive review PS: Accepted answer correct! – sandbox992 Mar 19 '21 at 15:25
8

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

return (
  <div className="App" ref={this.test}>
    current value : {this.test.current + ""}
  </div>
);
AKX
  • 152,115
  • 15
  • 115
  • 172
8

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

Neeraj Sewani
  • 3,952
  • 6
  • 38
  • 55
3

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.

Rojo
  • 2,749
  • 1
  • 13
  • 34
  • 2
    React documentation would encourage you to use `useLayoutEffect` in this specific case. Also, it's probably safer to log `kindRef.current`, as the console logs are not static. Finally, It might be a good practice to always specify the dependency array in hooks like useEffect – Sharcoux Aug 11 '21 at 22:38
  • 1
    As you noticed, I edited your question to better fit the [answering guidelines](https://stackoverflow.com/help/how-to-answer). Your original answer included several grammar mistakes. You also specifically asked for upvotes, which is something that is also against the rules. Finally, you uselessly signed your name in a codeblock. Your username is displayed at the bottom right corner of your answer (above the comments). Please do not rollback your answer to it's original version; I have made changes that are keeping your answer aligned to the guidelines and preventing it from being removed. – Rojo Aug 28 '21 at 12:50
  • Thanks bro , I were on hurry – ammar khaled Aug 30 '21 at 17:53
1

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]);
MikeL
  • 2,756
  • 2
  • 23
  • 41
  • I was racking my brain for 5 hours and I just needed to get this done. Thank you very much. Reports from a BackEnd developer trying to program React. – Eduardo Mior May 09 '23 at 17:16
-2
<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

Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62