parrisneeds.coffee code, music, drinks

React and Modals

You know what I miss? The ability to launch a dialog, alert or prompt and have it block execution of the current function. I miss the ability to just write var answer = window.prompt('What is your favorite pizza?'); and get the data I need immediately. For the past few years, we as a JS community have probably created a few thousand implementations for modals and the only thing we can agree on is that they all leave something to be desired.

Today, I stumbled upon a new solution, I think, a better solution. What if, getting your data back from a modal was this simple?

let { title, desc } = await createBlockingModal(MyModal);

Personally, I think it is ideal. What we all want is to create modals faster. Hooking a modal into global redux or local react state is a bit tedious. So why not create something a little less stateful?

Soooo where do we start? First off, let's create something that generates a component:

async function createBlockingModal(Component) {
    const modalEl = document.createElement('div');
    document.querySelector('body').append(modalEl);

    return new Promise((resolve, reject) => {
        ReactDOM.render(
            <Component
                onOk={(data) => {
                    ReactDOM.unmountComponentAtNode(modalEl);
                    modalEl.remove();
                    resolve(data);
                }}
                onCancel={(data) => {
                    ReactDOM.unmountComponentAtNode(modalEl);
                    modalEl.remove();
                    reject(data);
                }}
            />,
            modalEl
        );
    });
}

The above code defines how our modal is created, rendered onto the page, subsequently closed, and how the data is returned to the calling function. The promise returned here allows us to later await on the promise being resolved or rejected. Which means we can do something like this:

async function handleClick() {
    try {
        let { title, desc } = await createBlockingModal(MyModal);
    } catch (e) {
        console.log('The user canceled');
    }
}

So, what does the modal look like? Well that's the beauty, your modal can look like anything. Here's a really simple example:

const MyModal = props => (
    <div className="modal-overlay">
        <div className="modal">
            <form onSubmit={(e) => {
                e.preventDefault();
                const title = e.target.querySelector('[name=title]');
                const desc = e.target.querySelector('[name=desc]');
                props.onOk({
                    title: title.value,
                    desc: desc.value,
                });
            }}>
                <input type="text" name="title" />
                <input type="text" name="desc" />
                <input type="submit" value="Save" />
            </form>
        </div>
    </div>
);

Of course you could get fancier and split the modal and the form apart. My main point is that we can get the nice looking API with very little risk, in a really clean/reusable form factor.

Soapbox Time

When I first started using React, I was amazed at how fast I could crank out features. There was a period of time where we as a community realized that things were getting a bit messy so we started managing our state using systems like Redux. We all love Redux, it is amazing, but I think we all get a bit carried away with turning everything into global state. We've forgotten that we are trying to optimize for code cleanliness and more importantly dev speed. Creating systems like the one above let's us get back to a place where writing APIs for our fellow developers is as important as getting the job done.