This article focuses on using the React. UseRef () hook to create persistent mutable variables (also known as references or refs) and access DOM elements.

1. Variable variables

UseRef (initialValue) takes a parameter as an initialValue and returns a reference, ref, with a special property current, which remains constant throughout the life of the component. We can access ref directly with reference.current and update the value of ref with reference.current = newValue, for example:

import { useRef } from 'react';

function MyComponent() {
  const reference = useRef(initialValue);
  
  const someHandler = () = > {
    / / get the value
    const value = reference.current;
    
    / / update the value
    reference.current = newValue;
  };
  
  // ...
}
Copy the code

There are two things to note about referencing a REF:

  1. The value of ref remains the same when the component is re-rendered.
  2. Updating the value of ref does not trigger component re-rendering.

For example 🌰 : count button clicks and view page renders.

import { useRef } from 'react';

function LogButtonClicks() {
  const countRef = useRef(0);
  
  const handle = () = > {
    countRef.current++;
    console.log('Click the button${countRef.current}Time `);
  };

  console.log('I rendered! ');

  return <button onClick={handle}>My PM me</button>;
}
Copy the code

After executing the code, it’s not hard to see that no matter how many times the button is clicked, the console will only print ‘I rendered! ‘, which supports the first point.

The same example uses the useState() hook for the same functionality.

import { useState } from 'react';

function LogButtonClicks() {
  const [count, setCount] = useState(0);
  
  const handle = () = > {
    const updatedCount = count + 1;
    console.log('Click the button${updatedCount}Time `);
    setCount(updatedCount);
  };

  console.log('I rendered! ');

  return <button onClick={handle}>My PM me</button>;
}
Copy the code

As you can see from the execution, every time you click, the console prints’ I rendered! ‘, which means that the component is re-rendered every time the state is updated. Thus, the two main differences between a state and a reference are:

  1. Updates trigger component rerendering, but update references do not.
  2. Status updates are asynchronous (state variables are updated after rerendering), while references are updated synchronously (updated values are immediately available).

In real development, state can be used to store information that is presented directly on the page, and references can be used to store component infrastructure data, such as timers:

import { useRef, useState, useEffect } from 'react';

function StopWatch() {
  const timerIdRef = useRef(0);
  const [count, setCount] = useState(0);

  const startHandler = () = > {
    if (timerIdRef.current) { 
        return; 
    }
    timerIdRef.current = setInterval(
        () = > setCount(c= > c+1),1000);
  };

  const stopHandler = () = > {
    clearInterval(timerIdRef.current);
    timerIdRef.current = 0;
  };

  useEffect(() = > {
    return () = > clearInterval(timerIdRef.current); } []);return (
    <div>
      <div>Timer: {count}s</div>
      <div>
        <button onClick={startHandler}>start</button>
        <button onClick={stopHandler}>The end of the</button>
      </div>
    </div>
  );
}
Copy the code

In this component, startHandler() is called when the Start button is clicked, starting the count and saving the timer ID in a reference. When the “Stop” button is clicked. Execute stopHandler() to stop the timer and clear the timer. In addition, useEffect() return also stops the timer when the component is unloaded.

2. Access DOM elements

Another common use of useRef() is to access DOM elements in three steps:

  1. Define references to access elementsconst elementRef = useRef();
  2. Assign attributes to elements referencing ref:<div ref={elementRef}></div>;
  3. After mounting, passelementRef.currentAccess the corresponding DOM element.
import { useRef, useEffect } from 'react';

function OneElement() {
  const elementRef = useRef();

  useEffect(() = > {
    constdivElement = elementRef.current; } []);return (
    <div ref={elementRef}>I'm a DIV element</div>
  );
}

Copy the code

Another common example is 🌰 : the autofocus form

import { useRef, useEffect } from 'react';

function InputFocus() {
  const inputRef = useRef();

  useEffect(() = > {
    console.log(1,inputRef.current); inputRef.current.focus(); } []);console.log(2,inputRef.current);

  return (
    <input 
      ref={inputRef} 
      type="text" 
    />
  );
}
Copy the code

When the component is mounted, the DOM element is retrieved via inputref.current and the node method focus() is called for automatic focus.

As you can see from the console, the node at 2 is undefined because during initial rendering, the DOM structure of the page has not yet been created and React is not yet sure what the output of the component will be. In useEffect(callback, []), the INPUT element is created in the DOM, and the hook calls the callback immediately after it is mounted: therefore, the callback can access the value normally.

3. Update the limit of reference ref values

As you can see from InputFocus (2), when getting a ref value in a function component or calling a bound element method, you need to get the right value in the right place with the component lifecycle in mind. Ref (including updating state) should not be operated on in direct scope, but must be operated on inside a method event within the useEffect() callback.

import { useRef, useEffect } from 'react';

function MyComponent({ props }) {
  const myRef = useRef(0);

  useEffect(() = > {
    myRef.current++; // Good!

    setTimeout(() = > {
      myRef.current++; // Good!
    }, 1000); } []);const handler = () = > {
    myRef.current++; // Good!
  };

  myRef.current++; // Bad!

  if (props) {
    myRef.current++; // Bad!
  }

  return <button onClick={handler}>My PM me</button>;
}
Copy the code