简体   繁体   中英

Proper way to build bootstrap modals and notifications in React JS?

I would like to have modals and notifications in my app and coming from using old jQuery Bootstrap, creating modals and notifications were really easy but now I am pretty confused on how to implement this in the virtual DOM using the react component system.

This is what I believe the standard react way to build modals in React within a component:

Index/Router Component >
    Main Layout Component >
        {...Page Components... }
            {...Child Component}
                {<Modal /> or <Notification />}

The issue with this is I dont want to constantly have to import and create a <Modal> or <Notification /> component within my sub components, instead maybe just call a utility function such as {app.notify({type: 'success', message: 'some message'})} or app.modal({...customconfig}) and have both defined within my Main layout component which get triggerd through any child components.

Any help on this would be great, thanks!

You do not need to keep your Modal component in a hierarchy. Your Modal component should be an independent component which would take appropriate props to decide what needs to be displayed. Eg

<Modal message={"This is my modal"} showOkCancel={true} showYesNo={false} handleOkYes={()=>console.log("OK clicked")} handleCancelNo={()=>console.log("Cancel clicked"} />

In the above example, the Modal accepts a number of props which would help it decide the message to display, the buttons to display and the actions that need to take on said button click.

This kind of a component can reside outside your component hierarchy and can be imported into any component that needs to show a modal. The parent component would just need to pass the appropriate props to show the modal.

Hope this helps.

So here is the approach I took to resolve this.

First here is how you want to structure the modal and notification components:

{Index/Router Component}
    {Main Layout Component <Modal /> or <Notification />}
        {...Page Components... }
            {...Child Component calls app.modal({...config}) or app.notify(...config)}

For notifications, I used a plugin called react-notification-system and for modal, I just wrote it myself.

Here is my code:

Layout.js

import React from "react";
import {Link} from 'react-router';
import NotificationSystem from 'react-notification-system';

import AppHeader from "#/ui/header/AppHeader";
import AppFooter from "#/ui/footer/AppFooter";

import Modal from "#/ui/modals/modal/Modal";

import "@/main.scss";
import './layout.scss';


export default class Layout extends React.Component {
    constructor(props) {
        super(props);
    }

    componentDidMount() {
        app.notify.clear = this.refs.notificationSystem.clearNotifications;
        app.notify = this.refs.notificationSystem.addNotification;
        app.modal = this.refs.modal.updateProps;
    }

    render() {
        return (
            <div class="app">
                <div class="header">
                    <AppHeader page={this.props.location.pathname.replace('/', '')}/>
                </div>
                <div class="body">
                    {this.props.children}
                </div>
                <div class="footer">
                    <AppFooter />
                </div>

                <NotificationSystem ref="notificationSystem" style={false} />
                <Modal ref="modal" />
            </div>

        );
    };
}

Modal.js

import React from "react";
import ReactDOM from 'react-dom';

import SVGInline from "react-svg-inline";
import {closeSvg} from '#/utils/Svg';

export default class Modal extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            showHeader: true,
            showFooter: false,
            title: "",
            size: '',
            className: '',
            id: '',
            footerContent: null,
            showSubmitBtn: true,
            showCancelBtn: true,
            cancelBtnText: "Cancel",
            successBtnText: "Save Changes",
            onModalClose: () => {},
            showModal: false,
            html: () => {}
        }

        this.updateProps = this.updateProps.bind(this);
        this.hideModal = this.hideModal.bind(this);
    }

    componentWillMount() {
        var self = this;

        var $modal = $(ReactDOM.findDOMNode(this));
    }

    componentDidUpdate(prevProps, prevState) {
        if(this.state.showModal) {
            $('body').addClass('modal-open');
        } else {
            $('body').removeClass('modal-open');
        }
    }

    componentWillUnmount() {
        // $('body').removeClass("modal-open");
    }

    componentWillReceiveProps(nextProps) {
        console.log(nextProps);
    }

    updateProps(args) {
        let merged = {...this.state, ...args};
        this.setState(merged);
    }

    hideModal() {
        this.setState({
            showModal: false
        });

        this.state.onModalClose();
    }

    buildFooter() {
        if(this.props.footerContent) {
            return (
                <div class="content">
                    {this.props.footerContent}
                </div>
            )
        } else if(this.props.showCancelBtn && this.props.showSubmitBtn) {
            return (
                <div class="buttons">
                    <button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>{this.props.cancelBtnText}</button>
                    <button type="button" class="btn btn-success">{this.props.successBtnText}</button>
                </div>
            );
        } else if(this.props.showCancelBtn) {
            return (<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>Close</button>);
        } else if(this.props.showSubmitBtn) {
            return (<button type="button" class="btn btn-success">Save changes</button>);
        }
    }

    render() {
        let {
            id,
            className,
            onModalClose,
            size,
            showHeader,
            title,
            children,
            showFooter,
            showModal,
            html
        } = this.state;

        return (
            <div class={`modal-wrapper`} >
                {
                    showModal ?
                        <div class={`modal fade in ${className}`} role="dialog">
                            <div class="bg" ></div>
                            <div class={`modal-dialog ${size}`}>
                                <div class="modal-content">

                                    { showHeader ?
                                        <div class="modal-header">
                                            <button type="button" class="close" data-dismiss="modal">
                                                <SVGInline svg={closeSvg} />
                                            </button>
                                            <h4 class="modal-title">{ title }</h4>
                                        </div> : '' }


                                    <div class="modal-body" >
                                        {html()}
                                    </div>

                                    {  showFooter ?
                                        <div class="modal-footer">
                                            { this.buildFooter() }
                                        </div> : ''
                                    }

                                </div>
                            </div>
                        </div>
                    : ''
                }
            </div>
        );
    }
}

then in any of your child components you can just call within your render function:

app.notify({
    message: message,
    level: 'error'
});

or

app.modal({
    showModal: true,
    className: "fullscreen-image-modal",
    size: "modal-lg",
    html: () => {
        return (<img src={listingManager.LISTINGS_PATH + imgUrl} />);
    }
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM