Access the methods of class components
Tags: webdev, react, tutorial, javascript
Published: 2023-12-02 09:53:56

In our previous post, we learned about the syntax of [callback refs](https://phuoc.ng/collection/react-ref/store-a-reference-with-callback-refs/) and how to use them to create an input counter component. Today, we'll explore another useful application of callback refs by building a real-life component: a modal.

A modal is a type of dialog box that appears in the foreground of a web page or application. It's perfect for displaying important information, asking for user input, or confirming an action before proceeding. Modals can also be used to showcase media files like images and videos.

There are many common uses for modals, including login and registration forms, displaying terms and conditions, showcasing product details, and confirming actions like deleting data. The great thing about using a Modal instead of a traditional alert box is that it provides more control over the appearance of the popup, allowing developers to customize it to fit their needs.

So are you ready to dive in and learn more about building modals with callback refs? Let's get started!

## Organizing modal markup

Let's begin our journey by organizing the markup for a modal. A modal typically includes an overlay with content placed inside it.

```tsx
<div className="modal__overlay">
    <div className="modal__content">
        {/* Content of the modal */}
    </div>
</div>
```

The overlay element in a modal is super important. It covers the whole page with a dark background, which makes the modal content stand out and keeps users from getting distracted by the rest of the page. Plus, it stops users from clicking on anything behind the modal, so they can only focus on what's inside. This helps create a feeling of exclusivity and directs attention to the main message or action in the modal.

To make all of this happen, just use this nifty CSS code for the overlay:

```css
.modal__overlay {
    background: rgba(30 41 59 / 0.7);

    position: fixed;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;

    align-items: center;
    display: flex;
    justify-content: center;

    cursor: pointer;
}
```

The `modal__overlay` CSS class creates the dark background that appears behind the modal content. We use the `background` property to set the color and opacity of the overlay. In this case, we're using a semi-transparent black color to achieve the desired effect.

To make sure the overlay covers the whole page, we use `position`, `top`, `left`, `height`, and `width` properties. By setting `position` to `fixed`, we ensure the overlay stays in place even when users scroll.

We center the modal content using the `align-items` and `justify-content` properties. These properties make sure the content is perfectly centered both horizontally and vertically.

Lastly, we set the `cursor` property to `pointer` to let users know they can click on the overlay to close the modal.

## Closing a modal

When it comes to closing a modal, there are three easy ways to do it:

-   Click on the dedicated "Close" button
-   Press the Escape key on your keyboard
-   Click on the overlay element

For the purposes of this post, we'll focus on the third option and show you how to use callback refs to make it happen. All you need to do is add two different refs to your overlay and content elements, and attach callbacks using the `ref` attribute:

```tsx
<div
    className="modal__overlay"
    ref={(ele) => (this.overlayEle = ele)}
    onClick={this.handleClickOverlay}
>
    <div
        className="modal__content"
        ref={(ele) => (this.contentEle = ele)}
    >
        ...
    </div>
</div>
```

In this code snippet, `overlayEle` and `contentEle` are variables that reference the overlay and content elements, respectively. When the user clicks on the overlay element, the `handleClickOverlay()` function is triggered.

It's important to note that clicking on the overlay will close the element, but if the clicked target is within the content element, nothing will happen. To check if the clicked target is within the content element, we can use the `contentEle.contains(clickedTarget)` function, where `clickedTarget` is the clicked element retrieved from the `target` property of the event object.

Here's an example of what the `handleClickOverlay()` function could look like. Don't worry about the `close()` function for now, we'll talk about it in the next paragraph.

```tsx
handleClickOverlay(e) {
    if (this.contentEle && !this.contentEle.contains(e.target)) {
        // Close the modal
        this.close();
    }
}
```

It's easy to keep track of whether the modal is open or closed. We simply use an internal boolean state, like `isOpened`. When the page first loads, the modal is closed, so the state is initially set to `false`.

```tsx
constructor(props) {
    super(props);
    this.state = {
        isOpened: false,
    };
}
```

We'll need two functions to handle opening and closing the modal. These functions will set the value for the `isOpened` state.

```tsx
close() {
    this.setState({ isOpened: false });
}

open() {
    this.setState({ isOpened: true });
}
```

To close the modal, click on the overlay area in the demo below. Please note that this is just for demonstration purposes and there is no way to reopen it.

{% codesandbox thp553 %} 

## Opening a modal

Modals are typically not displayed by default. Instead, we can use a button to trigger the modal. Imagine you have a button and a modal with specific content. When you click the button, the modal will open up.

```tsx
const handleClickOpenModal = () => {
    // Open the modal
};

return (
    <button onClick={handleClickOpenModal}>
        Open the modal
    </button>
    <Modal>
        ...
    </Modal>
);
```

Let's talk about opening a modal in the `handleClickOpenModal()` function.

If you have a basic understanding of object-oriented programming (OOP), you know that we can call public methods of a class once we create an instance of it. Here's an example: we can create an instance of a square and calculate its area by calling the `getArea()` function.

```js
class Square {
    constructor(side) {
        this.side = side;
    }

    getArea() {
        return Math.pow(this.side, 2);
    }
}

const square = new Square(5);
square.getArea();   // 25
```

If we take a step back, it's clear that our Modal is a class component. That means we can call its methods, like `open()` or `close()`, to open or close the modal, as long as we can retrieve its instance.

Thankfully, callback refs aren't just useful when we need to get a reference to a DOM element. They also work to **get the instance of a class component**. The syntax is the same: set a callback to the `ref` attribute.

To get started, let's declare the `modalInstance` variable and use a callback ref to set it to the modal it's attached to.

```tsx
let modalInstance;

return (
    <Modal ref={(modal) => modalInstance = modal}>
        ...
    </Modal>
);
```

In this example, the `modalInstance` variable is a reference to the Modal component. To open the modal, all you need to do is call the `open()` function. It's as simple as that!

```tsx
const handleClickOpenModal = () => {
    modalInstance.open();
};
```

Give the demo below a try. To get started, click the button to open the modal. When you're done, simply click the dark overlay behind it to close the modal. Feel free to repeat the process as many times as you'd like.

{% codesandbox jzds74 %} 

## The limitations of accessing methods in class components

While the approach of using callback refs may work in our example, there are several disadvantages to be aware of.

Firstly, this approach doesn't work with functional components. Since they don't have instances, we cannot access their methods in the same way as we do with class components. This means that using callback refs to get the instance of a functional component won't work.

Secondly, functional programming (FP) is generally better than object-oriented programming (OOP) because there are no side effects. Developing components with FP provides better composition capacity than OOP. Additionally, in JavaScript, there are no real concepts of private and public methods. As you get the instance of the Modal, you can see all of its methods. In general, this is not considered a good practice.

Lastly, this approach is not scalable. Since you have to create a separate variable to reference a modal, you might have to create a lot of variables if the page has many modals.

No need to worry, though. You can achieve the same result in functional components through other means. One way is to use React hooks, specifically the `useImperativeHandle` hook. This hook lets you expose certain functions from a functional component to its parent component. We'll explore these solutions in this series as well.

---

It's highly recommended that you visit the [original post](https://phuoc.ng/collection/react-ref/access-the-methods-of-class-components/) to play with the interactive demos.

If you found this series helpful, please consider giving the [repository](https://github.com/phuocng/master-of-react-ref) a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

If you want more helpful content like this, feel free to follow me:

-   [Twitter](https://twitter.com/_phuocng)
-   [GitHub](https://github.com/phuocng)