Reputation: 6085
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
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
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
Reputation: 4287
This may happen in the following circumstances:
this.test
from the question.
<Component ref={this.test} />
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)
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
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
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
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>
)
}
}
Upvotes: 52
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
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