在 React JS 中构建引导模式和通知的正确方法



我想在我的应用程序中拥有模态和通知,并且来自使用旧的jQuery Bootstrap,创建模态和通知非常容易,但现在我对如何使用反应组件系统在虚拟DOM中实现这一点感到非常困惑。

这就是我认为在组件内在 React 中构建模态的标准反应方式:

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

这样做的问题是我不想在我的子组件中不断导入和创建<Modal><Notification />组件,而可能只是调用一个实用程序函数,例如 {app.notify({type: 'success', message: 'some message'})}app.modal({...customconfig}) 并在我Main layout component中定义两者,这些组件通过任何子组件触发。

对此的任何帮助都会很棒,谢谢!

您不需要将Modal组件保留在层次结构中。您的Modal组件应该是一个独立的组件,它将采用适当的props来决定需要显示的内容。例如

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

在上面的例子中,Modal接受许多道具,这将有助于它决定要显示的消息、要显示的按钮以及需要对所述按钮单击执行的操作。

这种组件可以驻留在组件层次结构之外,并且可以导入到需要显示模式的任何组件中。父组件只需要传递适当的道具来显示模态。

希望这有帮助。

因此,这是我解决此问题的方法。

首先,下面是您希望如何构建模式和通知组件:

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

对于通知,我使用了一个名为 react-notification-system 的插件,对于模态,我只是自己编写的。

这是我的代码:

布局.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>
        );
    };
}

莫代尔.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>
        );
    }
}

然后在任何子组件中,您只需在渲染函数中调用:

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

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

最新更新