preface
With the product iteration, there are more and more bullet frames in the project, and there are more bullet frames shared by business modules. In the initial stage, it may not be a common business pop-up box. We only put it in the current business module. With the iterative upgrade, some modules will become common bullets. In short, a pop-up box can be used in multiple pages. Let's give an example of our scene.
There is such a preview pop-up box in the project, which has been stored in our business components. The contents are as follows
import React from 'react'; import {Modal} from 'antd'; const Preview = (props) => { const {visible, ...otherProps} = props; return( <Modal visible={visible} {...otherProps} ... // Other Props > <div>Preview the contents of the component</div> </Modal> ) }
Such a component is used in multiple business modules. Let's deal with this situation in different ways.
Introduction components of each module
Components are shared and can be used in various business modules.
Used in module A
import React, {useState} from 'react'; import Preview from './components/preview'; import {Button} from 'antd'; const A = () => { const [previewState, setPreviewState] = useState({ visible: false, ... // Other props, including pop-up frame props and parameters required for preview }); // Show pop ups const showPreview = () => { setPreviewState({ ...previewState, visible: true, }) } // Close the bullet box const hidePreview = () => { setPreviewState({ ...previewState, visible: false, }) } return (<div> <Button onClick={showPreview}>preview</Button> <Preview {...previewState} onCancel={hidePreview} /> </div>) } export default A;
Used in module B
import React, {useState} from 'react'; import Preview from './components/preview'; import {Button} from 'antd'; const B = () => { const [previewState, setPreviewState] = useState({ visible: false, ... // Other props, including pop-up frame props and parameters required for preview }); // Show pop ups const showPreview = () => { setPreviewState({ ...previewState, visible: true, }) } // Close the bullet box const hidePreview = () => { setPreviewState({ ...previewState, visible: false, }) } return (<div> B Business logic of module <Button onClick={showPreview}>preview</Button> <Preview {...previewState} onCancel={hidePreview} /> </div>) } export default B;
We found that the codes for opening and closing the pop-up are basically the same. If there are thirty or forty places in our system that need to introduce preview components, it will be a life-threatening task to maintain. There are too many places that need to be changed every time there are adjustments.
Put it into Redux for global management.
From the above, we can see that the business logic shown to be closed is repetitive. We put it into redux for unified management. First, modify the Preview component
import React from 'react'; import {Modal} from 'antd'; @connect(({ preview }) => ({ ...preview, })) const Preview = (props) => { const {visible} = props; const handleCancel = () => { porps.dispatch({ type: 'preview/close' }) } return( <Modal visible={visible} onCancel={handleCancel} ... // Other Props > <div>Preview the contents of the component</div> </Modal> ) }
Add state to redux to manage our state and handle some parameters
const initState = { visible: false, }; export default { namespace: 'preview', state: initState, reducers: { open(state, { payload }) { return { ...state, visible: true, }; }, close(state) { return { ...state, visible: false, }; }, }, };
Global import
We want to open our pop-up box in the module through dispatch. We need to import our components before loading these modules. We import components in Layout
import Preview from './components/preview'; const B = () => { return (<div> <Header>Top navigation</Header> <React.Fragment> // Where our global pop-up frames are stored <Preview /> </React.Fragment> </div>) } export default B;
Used in module A
import React, {useState} from 'react'; import Preview from './components/preview'; import {Button} from 'antd'; @connect() const A = (porps) => { // Show pop ups const showPreview = () => { porps.dispatch({ type: 'preview/show' payload: { ... Preview required parameters} }) } return (<div> <Button onClick={showPreview}>preview</Button> </div>) } export default A;
Used in module B
import React, {useState} from 'react'; import Preview from './components/preview'; import {Button} from 'antd'; @connect() const B = () => { // Show pop ups const showPreview = () => { this.porps.dispatch({ type: 'preview/show' payload: { ... Preview required parameters} }) } return (<div> <Button onClick={showPreview}>preview</Button> </div>) } export default B;
Put it into redux to manage the state. First inject the bullet box component into our global. When calling the business, we can operate our bullet box only through dispatch.
Plug in based injection into the business
Putting the state into redux, we have to implement the redux process and inject our bullet frame into the layout component every time. Can we not care about these things and use them directly in the business.
Create a bullet box tool class
class ModalViewUtils { // Constructor receives a component constructor(Component) { this.div = document.createElement('div'); this.modalRef = React.createRef(); this.Component = Component; } onCancel = () => { this.close(); } show = ({ title, ...otherProps }: any) => { const CurrComponent = this.Component; document.body.appendChild(this.div); ReactDOM.render(<GlobalRender> <Modal onCancel={this.onCancel} visible footer={null} fullScreen title={title || 'preview'} destroyOnClose getContainer={false} > <CurrComponent {...otherProps} /> </ZetModal> </GlobalRender>, this.div) } close = () => { const unmountResult = ReactDOM.unmountComponentAtNode(this.div); if (unmountResult && this.div.parentNode) { this.div.parentNode.removeChild(this.div); } } } export default ModalViewUtils;
Change Preview component
import React, { FC, useState } from 'react'; import * as ReactDOM from 'react-dom'; import ModalViewUtils from '../../utils/modalView'; export interface IModalViewProps extends IViewProps { title?: string; onCancel?: () => void; } // Specific logic of view component const ModalView: FC<IModalViewProps> = props => { const { title, onCancel, ...otherProps } = props; return <View isModal {...otherProps} /> } // Instantiate the tool class and pass in the matching component export default new ModalViewUtils(ModalView);
Used in module A
import React, {useState} from 'react'; import Preview from './components/preview'; import {Button} from 'antd'; const A = (porps) => { // Show pop ups const showPreview = (params) => { Preview.show() } return (<div> <Button onClick={showPreview}>preview</Button> </div>) } export default A;
Used in module B
import React, {useState} from 'react'; import Preview from './components/preview'; import {Button} from 'antd'; const B = () => { // Show pop ups const showPreview = () => { Preview.show(params) } return (<div> <Button onClick={showPreview}>preview</Button> </div>) } export default B;
In this way, we only care about the implementation of the pop-up box content. When calling, we directly introduce components and call the show method. We will not rely on redux or call local instance components, and control display and hiding.
Based on Umi plug-in, there is no need to introduce module components
With the help of umi plug-ins, we can uniformly inject the global pop-up frame into the plug-ins for direct use.
import React, {useState} from 'react'; import {ModalView} from 'umi'; import {Button} from 'antd'; const A = () => { // Show pop ups const showPreview = () => { ModalView.Preview.show(params) } return (<div> <Button onClick={showPreview}>preview</Button> </div>) } export default A
Conclusion
For the unified treatment of the global pop-up frame, if you have questions, please comment and communicate together.