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.
Oldest comments (3)
Your avanced axample is the same as the first one, thank you anyway!
Thank you for letting me know. I fixed the mistake!
Thanks for sharing :)