DEV Community

Cover image for Access the methods of class components
Phuoc Nguyen
Phuoc Nguyen

Posted on • Edited on • Originally published at phuoc.ng

Access the methods of class components

In our previous post, we learned about the syntax of 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.

<div className="modal__overlay">
    <div className="modal__content">
        {/* Content of the modal */}
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

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:

.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;
}
Enter fullscreen mode Exit fullscreen mode

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:

<div
    className="modal__overlay"
    ref={(ele) => (this.overlayEle = ele)}
    onClick={this.handleClickOverlay}
>
    <div
        className="modal__content"
        ref={(ele) => (this.contentEle = ele)}
    >
        ...
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

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.

handleClickOverlay(e) {
    if (this.contentEle && !this.contentEle.contains(e.target)) {
        // Close the modal
        this.close();
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

constructor(props) {
    super(props);
    this.state = {
        isOpened: false,
    };
}
Enter fullscreen mode Exit fullscreen mode

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

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

open() {
    this.setState({ isOpened: true });
}
Enter fullscreen mode Exit fullscreen mode

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.

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.

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

return (
    <button onClick={handleClickOpenModal}>
        Open the modal
    </button>
    <Modal>
        ...
    </Modal>
);
Enter fullscreen mode Exit fullscreen mode

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.

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

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

const square = new Square(5);
square.getArea();   // 25
Enter fullscreen mode Exit fullscreen mode

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.

let modalInstance;

return (
    <Modal ref={(modal) => modalInstance = modal}>
        ...
    </Modal>
);
Enter fullscreen mode Exit fullscreen mode

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!

const handleClickOpenModal = () => {
    modalInstance.open();
};
Enter fullscreen mode Exit fullscreen mode

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.

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 to play with the interactive demos.

If you found this series helpful, please consider giving the repository 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:

Top comments (0)