Реализовать компонент глобальной подсказки на основе React Toast

внешний интерфейс контейнер JavaScript React.js

прелюдия

текст

анализ спроса

  • Тосты не нужно отображать вместе со страницей, они вызываются всякий раз, когда это необходимо.
  • Toast — это легкий компонент подсказок, его подсказка не прерывает операции пользователя и автоматически закрывается после определенного периода подсказки.
  • Toast должен предоставлять несколько разных типов сообщений для разных сценариев использования.
  • Методы всплывающих сообщений должны быть достаточно краткими, чтобы избежать ненужной избыточности кода.

окончательный эффект:

пример вызова

Toast.info('普通提示')
Toast.success('成功提示', 3000)
Toast.warning('警告提示', 1000)
Toast.error('错误提示', 2000, () => {
    Toast.info('哈哈')
})
const hideLoading = Toast.loading('加载中...', 0, () => {
    Toast.success('加载完成')
})
setTimeout(hideLoading, 2000)

Реализация компонента

Компоненты тоста можно разделить на три части, а именно:

  • note.js: уведомление. Компонент без сохранения состояния отвечает только за отрисовку компонента, соответствующего информации подсказки в соответствии с параметрами, переданными родительским компонентом, то есть окном подсказки, которое в конечном итоге видит пользователь.
  • уведомление.js: Уведомление. Контейнер компонента «Уведомление» используется для сохранения компонента «Уведомление», существующего на странице, и предоставляет методы добавления и удаления компонента «Уведомление».
  • toast.js: управляет окончательным открытым интерфейсом и вызывает метод add компонента Notification в соответствии с информацией, переданной внешним миром, чтобы добавить на страницу информационный компонент подсказки.

Структура каталогов проекта выглядит следующим образом:

├── toast
│   ├── icons.js
│   ├── index.js
│   ├── notice.js
│   ├── notification.js
│   ├── toast.css
│   ├── toast.js

Для простоты понимания реализация начинается с внешней части тоста.

toast.js

Поскольку на странице нет элемента, связанного с компонентом Toast, чтобы вставить информацию подсказки, то есть компонент «Уведомление», на страницу необходимо сначала вставить компонент «Уведомление-контейнер» компонента «Уведомление» на страницу. Определите здесь функцию createNotification для отображения компонента Notification на странице и сохраните функции addNotice и destroy.

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)
        }
    }
}

Затем определите глобальную переменную уведомления для хранения объекта, возвращаемого createNotification. И определите внешние функции, которые передают параметры компоненту уведомлений при вызове этих функций. Поскольку на странице должен существовать только один компонент уведомления, вам нужно только судить о том, существует ли текущий объект уведомления каждый раз, когда вызывается функция, и нет необходимости создавать его повторно.

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)
    }
}

notification.js

Итак, внешняя работа завершена, и теперь необходимо завершить внутреннюю реализацию компонента Notification. Во-первых, в атрибуте состояния компонента Notification есть атрибут notes, который используется для сохранения информации о Notification, существующей на текущей странице. А компонент Notification имеет два метода, addNotice и removeNotice, которые используются для добавления и удаления информации извещения (далее сокращенно извещение) в извещения.

При добавлении уведомления необходимо использовать метод getNoticeKey, чтобы добавить к уведомлению уникальное значение ключа, а затем добавить его к уведомлениям. И согласно длительности, предоставленной параметром, установить таймер на автоматическое закрытие по истечении времени.Здесь оговорено, что если значение длительности меньше или равно 0, то сообщение будет не закрываться автоматически, а всегда будет отображаться. Последний метод возвращает вызывающему методу метод, удаляющий собственное уведомление, чтобы он мог немедленно отклонить уведомление по мере необходимости.

При вызове метода removeNotice уведомления будут проходиться в соответствии со значением переданного ключа, и если результат будет найден, сработает его callback-функция и она будет удалена из уведомлений.

Наконец, он проходит через массив Notices и передает свойство Notice компоненту Notice для завершения рендеринга, где rendered-guout используется для реализации входящей анимации компонента.

(Примечание. Что касается проблемы с отображением, когда на странице одновременно отображается несколько подсказок, схема, принятая в этой статье, напрямую заменяет предыдущую подсказку последней подсказкой, поэтому уведомление добавляется в код и напрямую записывается как уведомления[ 0] = уведомление вместо уведомлений .push(уведомление), если вы хотите получить эффект сосуществования нескольких подсказок на странице, вы можете изменить его самостоятельно.)

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

    getNoticeKey() {
        const { notices } = this.state
        return `notice-${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(() => {
                    this.removeNotice(notice.key)
                }, notice.duration)
            }
        }
        return () => { 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>
        )
    }
}

notice.js

Последний оставшийся компонент уведомления очень прост, вам нужно только вывести окончательный контент в соответствии с информацией, переданной компонентом уведомления. Вы можете играть со своим собственным стилем дизайна.

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-notice ${type}`}>
                <svg className="icon" aria-hidden="true">
                    <use xlinkHref={`#${icons[type]}`} />
                </svg>
                <span>{content}</span>
            </div>
        )
    }
}

Обновлено 18 августа 2005 г.

  • Настройте схему отображения нескольких подсказок на странице: разрешите одновременное существование нескольких подсказок на странице;
  • Исправлена ​​проблема, из-за которой метод удаления подсказки, возвращаемый при добавлении подсказки, на самом деле не вступал в силу;
  • Оптимизируйте стили компонентов и эффекты перехода.

Примечание. Основные изменения касаются методов addNotice и removeNotice в файле уведомления.js. Код в исходном тексте не был изменен, пожалуйста, обратитесь к измененному коду.Исходный код проекта.

Эпилог