Learn React Refs with some real world use cases

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.

Recent Articles

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.

What are React Refs?

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,

  • Auto Focus an input Element, Managing the Element behaviours
  • Binding the external library with a DOM element.
  • Accessing the Element properties for Interactions in the Page.

there are two important concepts that you often hear in react refs. they are,

  • Creating and Managing Refs.
  • Forwarding Refs.

Creating and Managing Refs

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"
2
3import Input from "./Input"
4export default function App() {
5 const inputRef = useRef(null)
6
7 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

Forwarding 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"
2
3const 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)
6
7 useEffect(() => {
8 if (inputRef) {
9 console.log("inputRef", inputRef)
10 inputRef.current.value = 123
11 }
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.

Sharing DOM element with multiple handlers

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"
4
5export default function App() {
6 const [dimensions, setDimensions] = useState({})
7
8 return (
9 <Measure
10 bounds
11 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"
2
3const [dragProps, drag] = useDrag({
4 item: { id: 1, left: position.left, top: position.top, type: "BOX" },
5 collect: monitor => ({
6 isDragging: monitor.isDragging(),
7 }),
8})
9
10const [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)
17
18 setPosition({
19 left: left,
20 top: top,
21 })
22 return undefined
23 },
24})

There are two important hooks, they are,

  • useDrag - this should bind with an DOM element that we want to drag.
  • useDrop - this should bind with an element where we need to drop the dragged element.

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.

Composable React Refs

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"
6
7export default function App() {
8 const [position, setPosition] = useState({
9 left: 0,
10 top: 0,
11 })
12
13 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 })
19
20 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)
27
28 setPosition({
29 left: left,
30 top: top,
31 })
32 return undefined
33 },
34 })
35
36 return (
37 <Measure
38 bounds
39 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 <div
49 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}

Demo

https://codesandbox.io/s/react-forward-refs-5ru3l

To Read More

How to build an Actionable data ta...

In this article, we will see how to build an Actionable data table using a react...

How to Integrate Google Sheet in No...

This article explains how to Integrated Google sheet with your Nodejs Applicatio...

Kubernetes for Nodejs developers

Do you keep hearing the word kubernetes in the tech community and you couldn't u...