Apr 21, 2020· 9 mins to read

Learn React Refs with some real world use cases


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

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

To access the DOM element using refs,

const 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,

import React, { useRef, useEffect } from "react";

import Input from "./Input";
export default function App() {
  const inputRef = useRef(null);

  return (
    <div className="App">
      <Input type="text" ref={inputRef} placeholder="Enter Element" />
    </div>
  );
}

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

import React, { forwardRef } from "react";

const Input = ({ type, value, placeholder }, ref) => {
  return (
    <div>
      <input type={type} ref={ref} value={value} placeholder={placeholder} />
    </div>
  );
};
export default forwardRef(Input);

App.js

import React, { useRef, useEffect } from "react";
import "./styles.css";
import Input from "./Input";
export default function App() {
  const inputRef = useRef(null);

  useEffect(() => {
    if (inputRef) {
      console.log("inputRef", inputRef);
      inputRef.current.value = 123;
    }
  }, [inputRef]);
  return (
    <div className="App">
      <Input type="text" ref={inputRef} placeholder="Enter Element" />
    </div>
  );
}

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.

import React, { useState } from "react";
import "./styles.css";
import Measure from "react-measure";

export default function App() {
  const [dimensions, setDimensions] = useState({});

  return (
    <Measure
      bounds
      onResize={(measure) => {
        setDimensions(measure.bounds);
      }}
    >
      {({ measureRef }) => (
        <div className="App" ref={measureRef}>
          <div>
            <h4>BOX</h4>
          </div>
        </div>
      )}
    </Measure>
  );
}

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.

import { useDrag, useDrop } from "react-dnd";

const [dragProps, drag] = useDrag({
  item: { id: 1, left: position.left, top: position.top, type: "BOX" },
  collect: (monitor) => ({
    isDragging: monitor.isDragging(),
  }),
});

const [dropProps, drop] = useDrop({
  accept: "BOX",
  drop: (item, monitor) => {
    console.log("item", item);
    const delta = monitor.getDifferenceFromInitialOffset();
    const left = Math.round(item.left + delta.x);
    const top = Math.round(item.top + delta.y);

    setPosition({
      left: left,
      top: top,
    });
    return undefined;
  },
});

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

import React, { useState } from "react";
import "./styles.css";
import Measure from "react-measure";
import { useDrag, useDrop } from "react-dnd";
import composeRefs from "@seznam/compose-react-refs";

export default function App() {
  const [position, setPosition] = useState({
    left: 0,
    top: 0,
  });

  const [dragProps, drag] = useDrag({
    item: { id: 1, left: position.left, top: position.top, type: "BOX" },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  const [dropProps, drop] = useDrop({
    accept: "BOX",
    drop: (item, monitor) => {
      console.log("item", item);
      const delta = monitor.getDifferenceFromInitialOffset();
      const left = Math.round(item.left + delta.x);
      const top = Math.round(item.top + delta.y);

      setPosition({
        left: left,
        top: top,
      });
      return undefined;
    },
  });

  return (
    <Measure
      bounds
      onResize={(measure) => {
        console.log(measure.bounds);
      }}
    >
      {({ measureRef }) => (
        <div className="App" ref={composeRefs(measureRef, drop)}>
          {dragProps.isDragging ? (
            <div ref={drag} />
          ) : (
            <div
              className="dragBox"
              ref={drag}
              style={{ left: position.left, top: position.top }}
            >
              <h4>BOX</h4>
            </div>
          )}
        </div>
      )}
    </Measure>
  );
}

Demo

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

Copyright © Cloudnweb. All rights reserved.