This article explains the react refs with some real world use cases. I have been working with react for past 2 years. From my learning and understanding of react, i would like to share my knowledge to the people who starts to learn react.
There are tons of article out there to explain the react refs or any other concepts of react. then why am i writing this one?. Well, not everyone will face the same kind of problem while building applications. you may face the same kind of problem that i faced in my experience.
So, this article will explain react ref with two real world problems that i faced in the development and how i solved those problem.
React ref is a way to access the DOM element in the react component. let me explain you with an use case.
Usually, React parent child interaction happens through props. To change the child component, usually props are modified which re-render the child component in react.
there are few cases where you need to modify the value or change the property of element without getting those into the react cycle. few examples are,
there are two important concepts that you often hear in react refs. they are,
Let's see how to create and manage refs in react. we can divide the timeline into two here. those are, before hooks and after hooks.
Before Hooks
In class component, refs are created using
1class MyComponent extends React.Component {2 constructor(props) {3 super(props)4 this.myRef = React.createRef()5 }6 render() {7 return <div ref={this.myRef} />8 }9}
To access the DOM element using refs,
1const node = this.myRef.current
Refs holds the DOM element property in current
value.
Read this article to Learn more about in details
After Hooks
After Hooks came in, things have been changed and lots of hooks are implemented including for refs in react.
In functional component, To create refs,
1import React, { useRef, useEffect } from "react"23import Input from "./Input"4export default function App() {5 const inputRef = useRef(null)67 return (8 <div className="App">9 <Input type="text" ref={inputRef} placeholder="Enter Element" />10 </div>11 )12}
useRef is the react hooks used to create and manage refs
There will be some situation when you need to pass the ref from child to parent. If you just pass it as a props in react. it won't work. A proper way to do this is to forward the ref from child to parent.
consider that you have a Custom input in a parent component. you want to access the DOM element of it in Parent. Let's say auto focus it. you can do that by forwarding the ref.
Input.js
1import React, { forwardRef } from "react"23const Input = ({ type, value, placeholder }, ref) => {4 return (5 <div>6 <input type={type} ref={ref} value={value} placeholder={placeholder} />7 </div>8 )9}10export default forwardRef(Input)
App.js
1import React, { useRef, useEffect } from "react"2import "./styles.css"3import Input from "./Input"4export default function App() {5 const inputRef = useRef(null)67 useEffect(() => {8 if (inputRef) {9 console.log("inputRef", inputRef)10 inputRef.current.value = 12311 }12 }, [inputRef])13 return (14 <div className="App">15 <Input type="text" ref={inputRef} placeholder="Enter Element" />16 </div>17 )18}
Enough about the theoretical concepts, let's see some real world use cases which i faced in my experience.
In an application, i faced a scenario where we needed to implement drag and drop inside a scrollable component.
To make it work, i wanted to know the dimensions div
element. To get that, i used a library called react-measure which gives us the dimensions of the element that it's wrapped upon.
1import React, { useState } from "react"2import "./styles.css"3import Measure from "react-measure"45export default function App() {6 const [dimensions, setDimensions] = useState({})78 return (9 <Measure10 bounds11 onResize={measure => {12 setDimensions(measure.bounds)13 }}14 >15 {({ measureRef }) => (16 <div className="App" ref={measureRef}>17 <div>18 <h4>BOX</h4>19 </div>20 </div>21 )}22 </Measure>23 )24}
On the above code, we wrapped our div
inside Measure render-props which returns a measureRef. measureRef props should bind with the element that we want to measure. Here, i bind that with that div
So far, everything went good. now i need to implement the drag and drop inside of it. I went with react-dnd. there are two popular libraries for react drag and drop as far as i know. they are,
I have used both of them in my experience. For this requirement, i went with React dnd. because,
React Beautiful Dnd works well if it is like kanban board(where Dnd happens in linear horizontal and vertical orientation).
React Dnd works well for every use case. only difference is that we need to maintain things in react dnd.
Let's implement React Dnd in our App.
1import { useDrag, useDrop } from "react-dnd"23const [dragProps, drag] = useDrag({4 item: { id: 1, left: position.left, top: position.top, type: "BOX" },5 collect: monitor => ({6 isDragging: monitor.isDragging(),7 }),8})910const [dropProps, drop] = useDrop({11 accept: "BOX",12 drop: (item, monitor) => {13 console.log("item", item)14 const delta = monitor.getDifferenceFromInitialOffset()15 const left = Math.round(item.left + delta.x)16 const top = Math.round(item.top + delta.y)1718 setPosition({19 left: left,20 top: top,21 })22 return undefined23 },24})
There are two important hooks, they are,
Here comes the problem, i already bind measureRef with the Div element where i need to drop the dragged element.
How can i bind two handlers to a single ref?. Any Idea?
Well, After some research i found a library which can help me to achieve that.
1import React, { useState } from "react"2import "./styles.css"3import Measure from "react-measure"4import { useDrag, useDrop } from "react-dnd"5import composeRefs from "@seznam/compose-react-refs"67export default function App() {8 const [position, setPosition] = useState({9 left: 0,10 top: 0,11 })1213 const [dragProps, drag] = useDrag({14 item: { id: 1, left: position.left, top: position.top, type: "BOX" },15 collect: monitor => ({16 isDragging: monitor.isDragging(),17 }),18 })1920 const [dropProps, drop] = useDrop({21 accept: "BOX",22 drop: (item, monitor) => {23 console.log("item", item)24 const delta = monitor.getDifferenceFromInitialOffset()25 const left = Math.round(item.left + delta.x)26 const top = Math.round(item.top + delta.y)2728 setPosition({29 left: left,30 top: top,31 })32 return undefined33 },34 })3536 return (37 <Measure38 bounds39 onResize={measure => {40 console.log(measure.bounds)41 }}42 >43 {({ measureRef }) => (44 <div className="App" ref={composeRefs(measureRef, drop)}>45 {dragProps.isDragging ? (46 <div ref={drag} />47 ) : (48 <div49 className="dragBox"50 ref={drag}51 style={{ left: position.left, top: position.top }}52 >53 <h4>BOX</h4>54 </div>55 )}56 </div>57 )}58 </Measure>59 )60}
https://codesandbox.io/s/react-forward-refs-5ru3l
No spam, ever. Unsubscribe anytime.