foreplay

  • The API style refers to Ant Design’s Message global prompt

The body of the

Demand analysis

  • Toast does not need to be rendered along with the page, but is called whenever needed.
  • Toast is a lightweight prompt component whose prompt does not interrupt user actions and closes automatically after a certain amount of time.
  • Toast needs to provide several different message types to suit different usage scenarios.
  • Toast’s approach must be concise enough to avoid unnecessary code redundancy.

End result:

  • Click here to preview

Invoke the sample

Toast.info('General tips')
Toast.success('Success hint'.3000)
Toast.warning('Warning'.1000)
Toast.error('Error message'.2000, () => {
    Toast.info('ha ha')})const hideLoading = Toast.loading('Loading... '.0, () => {
    Toast.success('Load complete')
})
setTimeout(hideLoading, 2000)
Copy the code

Component implementation

The Toast component can be divided into three parts:

  • Notice. Js: notice. Stateless component, only responsible for rendering according to the parameters passed by the parent component into the corresponding prompt message component, that is, the user finally see the prompt box.
  • Notification. Js: notification. A container for the Notice component that holds the Notice components that exist on the page and provides methods for adding and removing Notice components.
  • Toasts. Js: Controls the interface that is eventually exposed to the outside world, and calls the add method of the Notification component to add the prompt message component to the page according to the information passed by the outside world.

The project directory structure is as follows:

├ ─ ─ toast │ ├ ─ ─ the ICONS, js │ ├ ─ ─ index. The js │ ├ ─ ─ notice. Js │ ├ ─ ─ notification. Js │ ├ ─ ─ toast. CSS │ ├ ─ ─ toast. JsCopy the code

For ease of understanding, the implementation starts with the external toast section.

toast.js

Since there are no elements related to the Toast component in the page, in order to insert a prompt message in the page, namely the Notice component, the Notification component of the Notice component needs to be inserted into the page first. Here we define a createNotification function that renders the Notification component on the page, and keep the addNotice and destroy functions.

function createNotification() {
    const div = document.createElement('div')
    document.body.appendChild(div)
    const notification = ReactDOM.render(<Notification />, div)
    return {
        addNotice(notice) {
            return notification.addNotice(notice)
        },
        destroy() {
            ReactDOM.unmountComponentAtNode(div)
            document.body.removeChild(div)
        }
    }
}
Copy the code

We then define a global Notification variable to hold the object returned by createNotification. And define exposed functions that pass parameters back to the Notification component when called. Because only one Notification component needs to exist on a page, you only need to determine whether the current Notification object exists each time the function is called.

let notification
const notice = (type, content, duration = 2000, onClose) = > {
    if(! notification) notification = createNotification()return notification.addNotice({ type, content, duration, onClose })
}

export default {
    info(content, duration, onClose) {
        return notice('info', content, duration, onClose)
    },
    success(content, duration, onClose) {
        return notice('success', content, duration, onClose)
    },
    warning(content, duration, onClose) {
        return notice('warning', content, duration, onClose)
    },
    error(content, duration, onClose) {
        return notice('error', content, duration, onClose)
    },
    loading(content, duration = 0, onClose) {
        return notice('loading', content, duration, onClose)
    }
}
Copy the code

notification.js

This completes the external work and then completes the internal implementation of the Notification component. Firstly, the state attribute of the Notification component contains a preceding Notice attribute, which is used to store information about the Notice existing in the current page. In addition, the Notification component has two methods, addNotice and removeNotice, which are used to add and removeNotice information from the Notice Notice.

To add a notice, use the getNoticeKey method to add a unique key value to the notice and then add it to the notice. In addition, according to the duration provided by the parameter, set the timer to automatically close it when it reaches the time. Here, if the value of duration is less than or equal to 0, the message will not be automatically closed, but always displayed. Finally, the method returns a method that removes its notice to the caller so that it can close the prompt immediately if needed.

When the removeNotice method is called, it traverses the preceding notice based on the value of the passed key. If the result is found, its callback function is triggered and removed from the notice.

Finally, the notice attribute is passed to the notice component to complete the rendering. Here, the react-transition-group is used to animate the entry and exit of the component.

(note: As for the display problem when there are multiple prompts on the page at the same time, the scheme adopted in this paper directly replaces the previous one with the latter one, so adding notice in the code is written as notice instead of notice. You can modify the effect if you want multiple prompts to coexist on the page.

class Notification extends Component {
    constructor() {
        super(a)this.transitionTime = 300
        this.state = { notices: []}this.removeNotice = this.removeNotice.bind(this)
    }

    getNoticeKey() {
        const { notices } = this.state
        return `notice-The ${new Date().getTime()}-${notices.length}`
    }

    addNotice(notice) {
        const { notices } = this.state
        notice.key = this.getNoticeKey()
        if (notices.every(item= >item.key ! == notice.key)) { notices[0] = notice
            this.setState({ notices })
            if (notice.duration > 0) {
                setTimeout((a)= > {
                    this.removeNotice(notice.key)
                }, notice.duration)
            }
        }
        return (a)= > { this.removeNotice(notice.key) }
    }

    removeNotice(key) {
        this.setState(previousState= > ({
            notices: previousState.notices.filter((notice) = > {
                if (notice.key === key) {
                    if (notice.onClose) notice.onClose()
                    return false
                }
                return true
            })
        }))
    }

    render() {
        const { notices } = this.state
        return (
            <TransitionGroup className="toast-notification">
                {
                    notices.map(notice => (
                        <CSSTransition
                            key={notice.key}
                            classNames="toast-notice-wrapper notice"
                            timeout={this.transitionTime}
                        >
                            <Notice {. notice} / >
                        </CSSTransition>))}</TransitionGroup>)}}Copy the code

notice.js

The last remaining Notice component is simple, just output the final content based on the information passed by the Notification component. You can create your own design style.

class Notice extends Component {
    render() {
        const icons = {
            info: 'icon-info-circle-fill'.success: 'icon-check-circle-fill'.warning: 'icon-warning-circle-fill'.error: 'icon-close-circle-fill'.loading: 'icon-loading'
        }
        const { type, content } = this.props
        return (
            <div className={`toast-noticeThe ${type} `} >
                <svg className="icon" aria-hidden="true">
                    <use xlinkHref={` # ${icons[type`]}} / >
                </svg>
                <span>{content}</span>
            </div>)}}Copy the code

The 18-08-05 update

  • Adjust the display scheme of multiple prompts on the page to: Allow multiple prompts on the page.
  • Fixed the problem that the removal prompt method returned when adding prompt does not actually take effect.
  • Optimize component styles and transitions.

Note: The main changes are addNotice and removeNotice methods in notification.js. The original code has not been modified. Please refer to the project source code for the modified code.

conclusion

  • Program source code
  • Project documentation