In the official React definition “Refs provide a way to access DOM nodes or React elements created in the render method”.
In this article I will show you how Refs can be used for other purposes.
I will not dwell on the explanation of the concept or the various methods of creating refs. You can find all this on the following link: https://reactjs.org/docs/refs-and-the-dom.html
Standard use case
In a common scenario we can use refs like this:
function NormalUsage() {
const inputRef = React.useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log(inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" name="input" ref={inputRef} />
<button> Submit form </button>
</form>
);
}
In this case, ref is used as a reference to a DOM Input. inputRef allow us access to input properties like value.
Advanced use — Timeout
The next example is a simple notice component. The notice can be closed by clicking a button, otherwise it will automatically close after a time limit.
const TIMEOUT_LIMIT = 5000
function Notice() {
const [isVisible, setIsVisible] = React.useState(true)
const timeoutRef = React.useRef(null)
const hideNotice = () => {
setIsVisible(false)
}
const forceHideNotice = () => {
clearTimeout(timeoutRef.current)
hideNotice()
}
React.useEffect(() => {
timeoutRef.current = setTimeout(hideNotice, TIMEOUT_LIMIT)
return () => {
clearTimeout(timeoutRef.current)
}
}, [])
return isVisible ? (
<div className="Notice">
<div>Notice content</div>
<button onClick={forceHideNotice}>Close</button>
</div>
) : null
}
In this case the ref is used to store a mutable data: the timeout ID
The steps are:
Create the ref (line 6)
Assign the timeout value to ref (line 18)
Clear timeout:
- If notice is closed by the user (line 13)
- When component is unmounted (line 21)
Advanced use #2— RequestAnimationFrame
The next example is a simple component that renders a div with an animation on the height.
const HEIGHT_LIMIT = 100
function Animated() {
const divRef = React.useRef()
const requestRef = React.useRef()
const heightRef = React.useRef(0)
const animate = () => {
heightRef.current += 1
divRef.current.style.height = `${heightRef.current}px`
if (heightRef.current < HEIGHT_LIMIT) {
requestRef.current = requestAnimationFrame(animate)
}
}
React.useEffect(() => {
requestRef.current = requestAnimationFrame(animate)
return () => cancelAnimationFrame(requestRef.current)
}, [])
return <div ref={divRef} style={{ backgroundColor: "#000" }} />
}
In this example 3 refs was created:
divRef: A classical use of refs. I’ts a reference to the Div element below.
requestRef: It contains the requestAnimationFrame id
heightRef: It contains the updated height value
The animate function is called until heightRef.current reaches the value of HEIGHT_LIMIT. heightRef.current is incremented by 1 each time the function is called.
If the component will be unmounted before heightRef.current reaches the value of HEIGHT_LIMIT, cancelAnimationFrame function will be executed
Advanced use #3 — Custom Hook
The next example creates a hook that returns a ref containing the x, y position of the mouse. With the help of the requestAnimationFrame a rounded div is animated to follow the mouse
//Hook
function useMousePosition() {
const mousePosition = React.useRef({ x: 0, y: 0 })
React.useEffect(() => {
const updateMousePosition = (ev) => {
mousePosition.current = { x: ev.clientX, y: ev.clientY }
}
window.addEventListener("mousemove", updateMousePosition)
return () => window.removeEventListener("mousemove", updateMousePosition)
}, [])
return mousePosition
}
//Component
function Point() {
const mousePosition = useMousePosition()
const requestRef = React.useRef()
const pointRef = React.useRef()
const animate = () => {
pointRef.current.style.left = `${mousePosition.current.x}px`
pointRef.current.style.top = `${mousePosition.current.y}px`
requestRef.current = requestAnimationFrame(animate)
}
React.useEffect(() => {
requestRef.current = requestAnimationFrame(animate)
return () => cancelAnimationFrame(requestRef.current)
}, [])
return (
<div
ref={pointRef}
style={{
position: "fixed",
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: "#000",
transform: "translate(-50%, -50%)"
}}
/>
)
}
The usePosition hook adds a mouseMove event to the window and stores the clientX and clientY values. In the animate function of the Point component the values x and y are used to animate the top and left properties of the div referenced by pointRef
Conclusion
React refs are extremely useful for saving mutable data without triggering a re-render of the component. They are powerful even when used in custom hooks and guarantee high performance.