useRef( ) : DOM and beyond…

This blog assumes that you know the React fundamentals and useState hook.

Read my article on what is a state in an App? and useState hook (there’s a TL;DR section, if you want to have a quick look ?), you would need some of that state-related concept…

This blog assumes that you know the React fundamentals and useState hook.

Read my article on what is a state in an App? and useState hook (there’s a TL;DR section, if you want to have a quick look ?), you would need some of that state-related concept in this blog.



What is useRef?

Ref is just a { current: initialValue } object. It’s nothing special. Both useRef(initivalValue) and createRef() give you that. – Dan Abramov

image.png

function useRef(initialValue) {
  // useRef works more like this
  return React.useState({
    current: initialValue
  })[0]
}

You just created your own useRef. ???



Why do we need useRef?

useRef actually serves two purposes,

  • Provides a reference to the DOM elements
  • Returns mutable value which persists across renders



But, what is this mutable and persistent value?

Persistent value is the kind of value that stays persistent between renders, that’s what useState returns, a persistent value (state) and updater API (setState) to update that state which causes a re-render for that component. for an application to update its View(UI) you need that setState API.



But what if you want to have a value that stays persistent and does not cause a re-render of a Component.?

This is such a fundamental need that react provides a built-in API for it, That’s what useRef is for.



useRef

// you can set any type of data as initialValue same as useState()
const objectWithCurrentProperty = React.useRef(initialValue)

const refOne = React.useRef() // returns {current : undefined}
const refTwo = React.useRef(1) // returns {current : 1}
const refThree = React.useRef([]) //returns {current : []}

useRef takes the initial value as an argument for the returned value.
These return values would be persisted and you can also mutate them according to your need.



Accessing the DOM with useRef

There are some libraries (e.g. Three.js or Anime.js) that need access to the dom.
when we write jsx it gets converted into React.createElement. a <div>Hello World</div> which we write as jsx gets converted into React.createElement("div", null, "Hello World") so you don’t have any direct access to the DOM nodes from your returned jsx.

So to get access to the DOM, you need to ask React to give you access to a particular DOM node when it renders your component. The way this happens is through a special prop called ref.

function UploadButton({ handleUpload }) {
  const inputRef = React.useRef();

  const handleClick = () => inputRef.current.click();

  return (
    <>
      <input type="file" hidden ref={inputRef} onInput={handleUpload} />
      <button onClick={handleClick}>Upload</button>
    </>
  );
}

In this example, we are passing the ref prop inputRef to the hidden file input, and when we click on a button that uses inputRef.current to get access to that DOM element of that input and on that element we are calling click event.

some other cases would be like getting a value from an input, changing focus, or selecting text.

react-hook-form is a form library that encourages uncontrolled way of handling forms and ref prop to handle forms in react component, give it a try it’s awesome.



Mutable Data Storage

Before we try to understand what this is and why do we need this? If you can, I would suggest you create a stopwatch component with stop and resume functionality.

.
.
.

Spoilers ahead….

.
.
.

// Here is a simple StopWatch component which updates time every 1 second
function StopWatch() {
  const [time, setTime] = useState(0);

  useEffect(() => {
      const interval = setInterval(() => {
        setTime((s) => s + 1);
      }, 1000);

      // clearInterval before unmounting component 
      return () => clearInterval(interval);
  }, []);

  return (<div>{time}</div>);
}

But now we need a button which will make the ticking of time stop and resume, for that we would add ticking state and update our useEffect.

function StopWatch() {
  const [time, setTime] = useState(0);
  const [ticking, setTicking] = useState(false);

  useEffect(() => {
    if (ticking) {
      const interval = setInterval(() => {
        setTime((ms) => ms + 1)
      }, 1000);
      return () => clearInterval(interval);
    } else {
      // ? but we don't have access "interval" here
      clearInterval(interval)
    }
  }, [ticking]);

  return (
    <div>
      <div>{time}</div>
      <button onClick={() => setTicking(c => !c)}>{ticking ? 'Pause' : 'Resume'}</button>
    </div>
  )
}

So where do we put our interval now? if you put this outside useEffect on every render all local variables would reset and it would become undefined again

function StopWatch() {
 ...
// I ? would keep becoming undefined on every re-render ?
let interval;

useEffect ...
}

So now we want something that stays persistent across renders and doesn’t cause re-renders, and you guessed it right, we need useRef here.

function StopWatch() {
  const [time, setTime] = useState(0)
  const [ticking, setTicking] = useState(false)
  // mutable and persistant 
  const interval = useRef()

  useEffect(() => {
    if (ticking) {
      // `interval` would not reset when component re-renders
      interval.current = setInterval(() => {
        setTime((ms) => ms + 1)
      }, 1000)
      return () => clearInterval(interval.current)
    } else {
      // and now you have access to the interval
      interval.current && clearInterval(interval.current)
    }
  }, [ticking])

  return (
    <div className="App">
      <h1>{time}</h1>
      <button onClick={() => setTicking(c => !c)}>
        {time === 0 ? 'Start' : ticking ? 'Pause' : 'Resume'}
      </button>
    </div>
  )
}

Where to go from here? from now you can go and read more about forwarding refs and useImperativeHandle. Thanks for reading.


Print Share Comment Cite Upload Translate
APA
Shubham Sananse | Sciencx (2024-03-29T09:49:34+00:00) » useRef( ) : DOM and beyond…. Retrieved from https://www.scien.cx/2021/04/28/useref-dom-and-beyond/.
MLA
" » useRef( ) : DOM and beyond…." Shubham Sananse | Sciencx - Wednesday April 28, 2021, https://www.scien.cx/2021/04/28/useref-dom-and-beyond/
HARVARD
Shubham Sananse | Sciencx Wednesday April 28, 2021 » useRef( ) : DOM and beyond…., viewed 2024-03-29T09:49:34+00:00,<https://www.scien.cx/2021/04/28/useref-dom-and-beyond/>
VANCOUVER
Shubham Sananse | Sciencx - » useRef( ) : DOM and beyond…. [Internet]. [Accessed 2024-03-29T09:49:34+00:00]. Available from: https://www.scien.cx/2021/04/28/useref-dom-and-beyond/
CHICAGO
" » useRef( ) : DOM and beyond…." Shubham Sananse | Sciencx - Accessed 2024-03-29T09:49:34+00:00. https://www.scien.cx/2021/04/28/useref-dom-and-beyond/
IEEE
" » useRef( ) : DOM and beyond…." Shubham Sananse | Sciencx [Online]. Available: https://www.scien.cx/2021/04/28/useref-dom-and-beyond/. [Accessed: 2024-03-29T09:49:34+00:00]
rf:citation
» useRef( ) : DOM and beyond… | Shubham Sananse | Sciencx | https://www.scien.cx/2021/04/28/useref-dom-and-beyond/ | 2024-03-29T09:49:34+00:00
https://github.com/addpipe/simple-recorderjs-demo