5

I've been using useRef in my function components as an alternative to class instance variables and I really like it. So typically it goes something like this:

First declare the variable, scrollRef for example:

const scrollRef = useRef()

Then, assign the value of some ref to scrollRef.current:

<ScrollView ref={ref => { scrollRef.current = ref }}>

// (Yeah, I'm doing mostly React Native)

Finally, use the ref:

<TouchableOpacity onPress={() => scrollRef.current.scrollToEnd()} />

This works fine. But when I have a lot of useRefs it seems like I have two problems:

  1. .current is all over the place
  2. there are some tricky cases and confusion when passing refs between components, when we're not immediately sure if we should read the value directly or read the .current key.

So I'm wondering if there is a better way.

I thought of declaring the variable with let instead of const, and then directly assign to .current:

let myRef = useRef().current

And then directly reading and mutating myRef

What are the drawbacks of this, are there cases where this would not work? Are there any better solutions to avoid using .current all over the place?

samzmann
  • 2,286
  • 3
  • 20
  • 47

2 Answers2

0

I'm late to this and not even a react expert. But if you create a variable pointing to current, then write a new object to that variable, then your variable is no longer pointing to ref.current. The only thing you can do that preserves that link is modifying properties of the variable.

Daniel
  • 1,695
  • 15
  • 33
0

There is a drawback to the useRef().current usage. Let's see below code(Using React v18.1.0):

import * as React from 'react';

export default function App() {
  const [_, forceUpdate] = React.useReducer((x) => x + 1, 0);

  const r1 = React.useRef<HTMLDivElement>();
  let r2 = React.useRef<HTMLDivElement>().current;

  const r3 = React.useRef<string>();
  let r4 = React.useRef<string>().current;

  React.useEffect(() => {
    console.log('[useEffect] r1: ', r1);
    console.log('[useEffect] r2: ', r2);
    console.log('[useEffect] r3: ', r3);
    console.log('[useEffect] r4: ', r4);
  });

  return (
    <div>
      <div ref={r1}>r1 test</div>
      <div
        ref={(ref) => {
          r2 = ref;
        }}
      >
        r2 test
      </div>

      <button
        onClick={() => {
          r3.current = 'cool';
          r4 = 'cool';
          console.log('[onClick] r3: ', r3);
          console.log('[onClick] r4: ', r4);
          forceUpdate();
        }}
      >
        click me
      </button>
    </div>
  );
}

After click the button, let's see the logs:

[onClick] r3: {current: "cool"}
[onClick] r4: cool
[useEffect] r1: {current: {…}}
[useEffect] r2: HTMLDivElement{attributes: {…}, innerHTML: "r2 test", nodeType: 1, tagName: "div"}
[useEffect] r3: {current: "cool"}
[useEffect] r4: undefined

I create two group refs for testing: r1 and r2, r3 and r4.

As you can see, the value of r4 in useEffect is not what we expect. It's an undefined rather than a 'cool' string. The value of r3 is correct.

stackblitz

Lin Du
  • 88,126
  • 95
  • 281
  • 483