React Hooks: Why is .current null for useRef Hook?

83,045

Solution 1

Ref.current is null because the ref is not set till after the function returns and the content is rendered. The useEffect hook fires every time the value of contents of the array passed to it changes. By passing observed in the array and by passing a callback that logs observed to the console, the useEffect hook can be leveraged to accomplish your task.

  useEffect(() => {
    console.log(observed.current);
  }, [observed]);

Edit: This only works on the first render as changes to a ref do not trigger re-renders. But the general statement about useEffect still holds. If you want to observe changes to a ref of a dom element, you can use a callback as ref.

  <div 
    ref={el => { console.log(el); observed.current = el; }} // or setState(el)
    className="App"
  >

Code Sandbox

Solution 2

This is what I ended up doing since calling useEffect on rlvRef did not capture all events.

const rlvRef = useRef()
const [refVisible, setRefVisible] = useState(false)

useEffect(() => {
  if (!refVisible) { 
    return
  }
  // detected rendering
}, refVisible)

return (
  <RecyclerListView
    ref={el => { rlvRef.current = el; setRefVisible(!!el); }}
    ... 
  />
)
Share:
83,045
Xen_mar
Author by

Xen_mar

Updated on March 22, 2021

Comments

  • Xen_mar
    Xen_mar about 3 years

    I have a simple example of a component:

    function App() {
      const observed = useRef(null);
      console.log(observed.current);
    
      return (
        <div ref={observed} className="App">
          <h1>Hello CodeSandbox</h1>
          <h2>Start editing to see some magic happen!</h2>
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    I would expect that observed.current would be of type element and current would not be empty but the div element with all its properties. My understanding would be:

    1. The ref is initialised with a value of null
    2. Null is overwritten by the ref

    But as it turns out, .current remains empty. This is bad since I want to pass observed to a function that expects an argument of type Element.

    https://codesandbox.io/embed/purple-forest-0460k

  • Xen_mar
    Xen_mar almost 5 years
    Great thanks! I am quite know to Hooks but for a functional component I would assume that the whole function is the "render function". Do you may have a reference of things I can and can't do in a render function?
  • Xen_mar
    Xen_mar almost 5 years
    Great thanks! I am quite know to Hooks but for a functional component I would assume that the whole function is the "render function". Do you may have a reference of things I can and can't do in a render function?
  • James
    James almost 5 years
    "You have to check for the value inside a useEffect hook" - this statement is not strictly true, I could have a memoised callback function that that can access a ref. The main point is the ref is null because the UI hasn't actually rendered yet, useEffect works here because useEffect it's the equivalent to componentDidMount and a few other hooks that run after the component has rendered.
  • Avin Kavish
    Avin Kavish almost 5 years
    I wasn't trying to imply that useEffect was the only hook. But for OP's use case there is no point in using a memoized callback. Because his task is to console.log. Yes I have mentioned that point in my text. yes, useEffect fires every time the contents of the array in the 2nd parameter changes.
  • Xen_mar
    Xen_mar almost 5 years
    the last comment helped a looooot in understanding this! Tahnks @James!
  • Xen_mar
    Xen_mar almost 5 years
    Can you guys specify what a memorised callback is? Is it a useMemo hook? Its not a term with lots of google results.
  • Avin Kavish
    Avin Kavish almost 5 years
    @Jame Changed text, should be clearer for future readers. reactjs.org/docs/hooks-reference.html#usememo
  • James
    James almost 5 years
    @AvinKavish edit looks much better IMO :) Xen_mar check out useCallback
  • wiktor
    wiktor almost 5 years
    I think useEffect will run only once because observed will never change even if observed.current changes. That is the point of useRef. "The returned object will persist for the full lifetime of the component." codesandbox.io/s/romantic-tharp-dsnpx
  • Mathijs Segers
    Mathijs Segers over 4 years
    In my case it has been set before, but a little bit later it becomes null again.
  • Weston Wedding
    Weston Wedding about 4 years
    Using refs as useEffect() dependencies is, imo, an antipattern. Any future changes to ref.current will not trigger the useEffect().
  • Avin Kavish
    Avin Kavish about 4 years
    @WestonWedding this is not a pattern at all, It's a simple and quick way to get the ref printed to the console because the useEffect hook runs after the first render. Yes, since refs exist outside react state, changes to the value will not trigger re-renders. Updated answer.
  • Yash Ojha
    Yash Ojha about 3 years
    worked like a charm. none of the other answers worked for me.
  • Ganesh
    Ganesh almost 3 years
    Cannot assign to 'current' because it is a read-only property
  • loekTheDreamer
    loekTheDreamer about 2 years
    this doesnt work. i dont understand why there are upvotes.
  • doublejosh
    doublejosh almost 2 years
    No, rlvRef.current is not a read only value. The reference is a const but .current is mutable.