How do we encapsulate the common bullet frame in the project

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.

Keywords: Front-end React

Added by beanman1 on Fri, 14 Jan 2022 04:17:21 +0200