The original link www.robinwieruch.de/react-ref
Use React Ref and really understand that it can be used in two different scenarios. To be honest, I’m not sure I’ve understood everything correctly so far, as it’s not used as often as states or side effects in React, and its API did change a lot in React’s past. In this React Ref tutorial, I want to give you a step-by-step introduction to refs in React.
REACT USEREF HOOK: REFS
React Refs is closely related to DOM. That used to be true, but not anymore since React introduced React Hooks. Ref means just a reference, so it can be a reference to anything (DOM nodes, JavaScript values,…). . So, before delving into its use with HTML elements, we’ll take a step back and first explore React Ref without the DOM.
React provides us with the React useRef Hook, which is the current API when using ref in the React function component. The useRef Hook returns a mutable object that remains unchanged for the life of the React component. Specifically, the returned object has a current property, which holds any modifiable value for us:
Take the React component as an example:
function Counter() {
const hasClickedButton = React.useRef(0);
let otherCount = 0;
const [count, setCount] = React.useState(0);
function onClick() {
const newCount = count + 1;
otherCount = otherCount + 1;
setCount(newCount);
hasClickedButton.current = hasClickedButton.current + 1;
}
console.log('clicked button? ' + hasClickedButton.current + 'tiems');
console.log(otherCount);
return (
<div>
<p>{count}</p>
<button type="button" onClick={onClick}>
Increase
</button>
</div>
);
}
Copy the code
The current property of ref is initialized with the argument we provided for the useRef hook (0 here). At any time, we can reassign the current attribute of ref to a new value. In the previous example, we simply kept track of how many times the button was clicked.
The thing about setting React Ref to a new value is that it does not trigger a re-rendering of the component (and the variables declared in it are 0 each time). While the state updater function in the previous example (setCount in this case) updates the state of the component and rerenders the component, simply toggling the Boolean value of the current attribute of ref does not trigger rerendering at all.
Well, we can use the React useRef Hook to create a mutable object that will exist for the entire life of the component. But every time we change it, it doesn’t trigger rerendering — because that’s what states are for — so what’s the ref usage here?
REACT REF AS INSTANCE VARIABLE
Ref can be used as an instance variable of a function component in React when we need to track some state without using the React rerender mechanism. For example, we can keep track of whether a component is rendered for the first time or re-rendered:
function ComponentWithRefInstanceVariable() { const [count, setCount] = React.useState(0); function onClick() { setCount(count + 1); } const isFirstRender = React.useRef(true); React.useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; }}); return ( <div> <p>{count}</p> <button type="button" onClick={onClick}> Increase </button> {/* Only works because setCount triggers a re-render. Just changing the ref's current value doesn't trigger a re-render. */} <p>{isFirstRender.current ? 'First render.' : 'Re-render.'}</p> </div> ); }Copy the code
Deploying instance variables for React components using refs is not widely used or often needed.
Rule of thumb: Whenever you need to track state in a React component that should not trigger component rerendering, you can create instance variables for it using the React useRef Hooks.
REACT USEREF HOOK: DOM REFS
Let’s take a look at React’s ref specialty: DOM. Most of the time, when you have to interact with HTML elements, you’ll use React’s ref. React is declarative in nature, but sometimes you need to read values from HTML elements, interact with HTML element apis, and even have to write values to HTML elements. In these rare cases, you must use the React Refs to interact with the DOM in an imperative rather than a declarative way.
The React component shows the most popular example of how React Ref and DOM API usage interact:
function App() {
return (
<ComponentWithDomApi
label="Label"
value="Value"
isFocus
/>
);
}
function ComponentWithDomApi({ label, value, isFocus }) {
const ref = React.useRef(); // (1)
React.useEffect(() => {
if (isFocus) {
ref.current.focus(); // (3)
}
}, [isFocus]);
return (
<label>
{/* (2) */}
{label}: <input type="text" value={value} ref={ref} />
</label>
);
}
Copy the code
As before, we use the React useRef Hook to create a ref object. In this case, we do not assign any initial value to it, because this will be done in the next step, and we provide the REF object to the HTML element as a ref HTML attribute. React will automatically assign the DOM node of this HTML element to the REF object for us. Finally, we can interact with the API using the DOM node (the current attribute now assigned to ref).
The previous example shows how to interact with the DOM API in React. Next, you’ll learn how to use ref to read values from DOM nodes. The following example reads the size from our element to display it as a title in our browser:
function ComponentWithRefRead() {
const [text, setText] = React.useState('Some text ...');
function handleOnChange(event) {
setText(event.target.value);
}
const ref = React.useRef();
React.useEffect(() => {
const { width } = ref.current.getBoundingClientRect();
document.title = `Width:${width}`;
}, []);
return (
<div>
<input type="text" value={text} onChange={handleOnChange} />
<div>
<span ref={ref}>{text}</span>
</div>
</div>
);
}
Copy the code
As before, we use the React useRef Hook to initialize the REF object and use it in the React JSX to assign the current ref attribute to the DOM node. The React useEffect Hook is used to read the width of the element rendered for the first time. You should be able to see the width of the element as the title in the browser TAB.
However, reading the size of the DOM node only happens during initial rendering. If you want to read it every time the state changes, because after all it changes the size of our HTML element, you can provide the state as a dependent variable to the React useEffect Hook. Whenever the state (in this case, text) changes, the element’s new size reads and writes to the document’s title property from the HTML element:
function ComponentWithRefRead() { const [text, setText] = React.useState('Some text ... '); function handleOnChange(event) { setText(event.target.value); } const ref = React.useRef(); React.useEffect(() => { const { width } = ref.current.getBoundingClientRect(); document.title = `Width:${width}`; }, [text]); return ( <div> <input type="text" value={text} onChange={handleOnChange} /> <div> <span ref={ref}>{text}</span> </div> </div> ); }Copy the code
Both examples use the React useEffect Hook to process ref objects. We can avoid this by using CALLBACK refs.
REACT CALLBACK REF
A better approach to the previous example is to use something called CALLBACK REF. With CALLBACK REF, you don’t have to use useEffect and useRef Hooks anymore because CALLBACK REF lets you access DOM nodes every time you render:
function ComponentWithRefRead() { const [text, setText] = React.useState('Some text ... '); function handleOnChange(event) { setText(event.target.value); } const ref = (node) => { if (! node) return; const { width } = node.getBoundingClientRect(); document.title = `Width:${width}`; }; return ( <div> <input type="text" value={text} onChange={handleOnChange} /> <div> <span ref={ref}>{text}</span> </div> </div> ); }Copy the code
The ref callback is nothing more than a function that can be used on the REF attribute of HTML elements in JSX. This function accesses DOM nodes and fires whenever it is used on the REF attribute of an HTML element. Essentially, it does the same thing as our previous side effect, but this time the ref callback itself tells us that it’s attached to the HTML element (without const ref = react.useref ();). .
Before using the useRef + useEffect combination, you could run your side effects at certain times with the hook dependency array of useEffect. You can achieve the same effect as the callback ref by enhancing it to run only on the first rendering of the component using React’s useCallback Hook:
function ComponentWithRefRead() { const [text, setText] = React.useState('Some text ... '); function handleOnChange(event) { setText(event.target.value); } const ref = React.useCallback((node) => { if (! node) return; const { width } = node.getBoundingClientRect(); document.title = `Width:${width}`; } []); return ( <div> <input type="text" value={text} onChange={handleOnChange} /> <div> <span ref={ref}>{text}</span> </div> </div> ); }Copy the code
You can also be more specific here with the dependency array of the useCallback hook. For example, the callback to ref is executed only when the state (in this case, text) changes, and of course for the first rendering of the component:
function ComponentWithRefRead() { const [text, setText] = React.useState('Some text ... '); function handleOnChange(event) { setText(event.target.value); } const ref = React.useCallback((node) => { if (! node) return; const { width } = node.getBoundingClientRect(); document.title = `Width:${width}`; }, [text]); return ( <div> <input type="text" value={text} onChange={handleOnChange} /> <div> <span ref={ref}>{text}</span> </div> </div> ); }Copy the code
REACT REF FOR READ/WRITE OPERATIONS
So far, we have only used DOM Refs for reading operations (such as reading the size of a DOM node). You can also modify the referenced DOM node (write). The next example shows how to use the React ref to apply styles without managing any additional React state for it:
function ComponentWithRefReadWrite() { const [text, setText] = React.useState('Some text ... '); function handleOnChange(event) { setText(event.target.value); } const ref = (node) => { if (! node) return; const { width } = node.getBoundingClientRect(); if (width >= 150) { node.style.color = 'red'; } else { node.style.color = 'blue'; }}; return ( <div> <input type="text" value={text} onChange={handleOnChange} /> <div> <span ref={ref}>{text}</span> </div> </div> ); }Copy the code
This can be done for any property on this reference DOM node. It is important to note that React should not normally be used in this way because it is declarative. Instead, you’ll use React’s useState Hook to set a Boolean value, whether you want to color the text red or blue. However, sometimes it can be helpful to manipulate the DOM directly for performance reasons while preventing re-rendering.
To learn about it, we can also manage state in the React component this way:
function ComponentWithImperativeRefState() { const ref = React.useRef(); React.useEffect(() => { ref.current.textContent = 0; } []); function handleClick() { ref.current.textContent = Number(ref.current.textContent) + 1; } return ( <div> <div> <span ref={ref} /> </div> <button type="button" onClick={handleClick}> Increase </button> </div> ); }Copy the code
Although it is not recommended to go down this rabbit hole…… Essentially, it should just show you how to manipulate any element in React using the React ref attribute and write operations. However, why do we have React and no longer use Vanilla JavaScript? React refs are mostly used for read operations.
This introduction should have shown you how to use React refs to reference DOM nodes and instance variables by using React useRef Hooks or callback refs.