The React ofuseRef

When useRef encountered some questions in learning React recently, he wrote them down.

useStateProblems encountered

Speaking of useRef, let’s take a look at the “problems” with useState.

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

export default function Demo() {
  const [like, setLike] = useState(0);

  useEffect(() = > {});

  const handleClick = () = > {
    setLike(like + 1);
  };

  const getLikeValue = () = > {
    setTimeout(() = > {
      alert(like);
    }, 2000);
  };

  return (
    <div>
      <button onClick={handleClick}>+</button>
      <button>{like} 👍</button>
      <button onClick={getLikeValue}>Like value obtained</button>
    </div>
  );
}

Copy the code

This is a very classic example of three buttons rendered on the page. When I click +, the page is rendered back to 1.

At this time, when I click the button to get the “Like” value, the alert will not be immediately issued due to the timer. At this time, I click + to modify “Like”.

When two seconds pass, you will notice that the latest “like” value is displayed on the page, and the “Like” displayed by Alert stays at 1.

First, the conclusion about this simple piece of code:

Every time a Demo function is run we call it “every render”, and every render function has its own independent function insidepropsandstateWhen injsxIn the call codestateWhen rendering, each render gets the values in its own render scopepropsandstate.

Of course this is not human language, let’s continue to read.

contrastvueDifference in renewal principle

In essence, the principle of responsivity is completely different from that of VUE. As we all know, in VUe3, the value of responsivity is modified through proxy. When the value of responsivity is modified, the corresponding set trap function will be triggered to trigger the update and the corresponding collected Effect will be run to update the template.

In React, the like in state is just a number defined in the render function. It’s not proxy, Watcher,effect… It’s just a number.

When we first call the function,like assigns the initialization value 0. When we click the button to call setLike, React will render the component again (running the Demo function). At this point, the internal like of the new function is 1, and the Demo function is re-called with this internal value to render the page. And so on, like this code:

const like = 2 // Final value
// ...
<p>{like}</p>
// ...

// During first render
function Counter () {
const like = 0
// ...
<p>{like}</p>
// ...
}

// After a Click, our function is called again
function Counter () {
const like = 1
// ...
<p>{like}</p>
// ...
} 

// After another click, our function is called again
function Counter () {
const like = 2
// ...
<p>{like}</p>
// ...
} 


// ...
Copy the code

Conclusion the analysis

Every time we update our status, (modifystateValue).reactComponents will be re-rendered, and each rendering will get a separate onelikeThe state value is a constant independent of each render function, and its function is only to render output, insertjsxIt’s just a number.

You might wonder where the “like” value comes from every time you call a function. The new “like” value is provided by React when we call setLike to change its value. React will re-run the function with new values to render again, ensuring that the render and output match.

The key point here is that the state/prop(intuitively like value) of any render cycle (function call) does not change over time, because the like value in each render function call is a constant (within the scope of the respective render function).

The render output changes because the component function is called from time to time, and the like values in the render function are independent of each other.

That’s why setTimeout still gets a 1 instead of the latest like. Because of the closure, when we click getLikeValue we get the internal like value of the next render function. Keep in mind that state and prop are independent of each other (since they are variables in the scope of their respective functions). State and prop are constant in each separate render function.

useRef

We talked about the independence of state and props in different renderings, which brings us to our main character, useRef. UseRef has two main functions in daily life. Let’s talk about the problem we encountered with state and how to use useRef to solve it.

useRefFunction 1: Links between multiple renders

React renderers /props are independent of each other, so it’s natural for us to think about how to generate relationships between renderers. This is where useRef shows what he can do.

Let’s look at the type definition of useRef’s react return value:

 interface MutableRefObject<T> {
        current: T;
  }
Copy the code

You can see that the useRef return value is an object with the attribute current of type stereotype

.

It differs from defining a {current: XXX} directly in function compoent.

UseRef keeps a unique reference to the returned value in all render. Because all assignments and values to ref are the final state, there is no different isolation in different render.

In simple terms, you can think of the return value of useRef as a global variable.

Let’s rewrite the Demo and see:

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

export default function Demo() {
  const [like, setLike] = useState(0);
  const likeRef = useRef(0);

  useEffect(() = > {});

  const handleClick = () = > {
    setLike(like + 1);
    likeRef.current = likeRef.current + 1;
  };

  const getLikeValue = () = > {
    setTimeout(() = > {
      alert(likeRef.current);
    }, 2000);
  };

  return (
    <div>
      <button onClick={handleClick}>+</button>
      <button>{like} 👍</button>
      <button onClick={getLikeValue}>Like value obtained</button>
    </div>
  );
}
Copy the code

Following the previous steps, at this point we can see that the value of Alert is the latest value. Instead of scoped – isolated values.

It is important to note that changing the value returned by useRef does not cause react to re-render the execution function. The rendering of the page in the demo was not caused by changing the Ref value, but by changing setLike in state while likeref. current.

Changing the value of the useRef does not cause the page to be re-rendered, which can do a lot of things. For example, you can useEffect to query whether a page is rendered for the first time or updated.

In summary, a change in the useRef return value does not cause a page update. And useRef, like react global variables, does not have the scoped isolation mechanism of state/props in different subrenders. This is the main difference between useRef and useState hooks.

useRefFunction two: Get the DOM element

vue3In the DOM

Of course, this is also a common use of useRef, as opposed to getting DOM nodes in vue3:

<template>
  <div ref="helloRef">Hello</div>
</template>
<script>
import { ref } from 'vue'
export default {
  setup() {
    const helloRef = ref(null)
    return {
      helloRef
    }
  }
}
</script>
Copy the code

All you need to do is get the div node with helloRef.value.

reactIn the DOM

Speaking of which, yes, useRef can also be used to retrieve DOM elements on a page.

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

export default function Demo() {
  const domRef = useRef<HTMLInputElement>(null);

  useEffect(() = >{ domRef.current? .focus();console.log(domRef,'domRef')});return (
    <div>
      <input ref={domRef} type="text" />
      <button>increase</button>
    </div>
  );
}

Copy the code
  1. throughuseRefCreate a variable to save (domRef).
  2. injsxThrough theref={domRef}Add attributes to the corresponding element node.
  3. Pass after the page is mounteddomRef.currentYou can get the actual DOM element of the node.

conclusion

For the Demo above, we can summarize some of useRef’s features.

We can think of the useRef return value as an internal component global shared variable that shares the same value within the render. Relative to state/props they are independent of the internal scope values in different sub-renders.

It is also important to note that changing the useRef return value does not cause the component to rerender, which is also different from state/props.

In the React. FunctionComponent, we can retrieve the actual JSX Dom element using useRef.