Статья изначально опубликована на Tinloof Отказ
В этой статье мы будем строить с нуля React уведомления (тосты) без использования какой-либо сторонней библиотеки (кроме реагирования).
Компонент уведомлений имеет следующие требования:
- Четыре вариации цвета: информация (синий), успех (зеленый), предупреждение (оранжевый) и ошибка (красный).
- Он расположен в правом верхнем углу экрана.
- Он анимирован, чтобы скользить, когда он добавляется и выдвигает, когда он удален. Другие уведомления должны скользить вертикально, когда уведомление удаляется.
- Я могу создавать уведомления, которые закрываются автоматически через 10 секунд.
- Я могу создавать уведомления, декларивно в JSX (например,
<Уведомление/>
). - Я могу неиспольстиваться уведомлениями, позвонив функцию (например, успех ()
).
Конечный исходный код можно найти здесь и демонстрация может быть просмотрена здесь Отказ
Я использовал Create-raction-app Для генерации котельной для этого проекта и CSS модули стильно это.
Вы можете использовать любые другие инструменты для генерации котельной и стиля компонента.
Вот наша структура каталогов, мы пройдемся через каждый файл в нем:
├── App.css ├── App.js ├── index.css ├── index.js └── notify ├── Notification | ├── Notification.module.css | ├── index.js | └── times.svg ├── createContainer | ├── container.module.css | └── index.js └── index.js
Компонент уведомлений
// notify/Notification/index.js import React from "react"; import PropTypes from "prop-types"; import cn from "classnames"; import { ReactComponent as Times } from "./times.svg"; import styles from "./Notification.module.css"; export default function Notification({ color = Color.info, children }) { return ({children}); } export const Color = { info: "info", success: "success", warning: "warning", error: "error", }; Notification.propTypes = { notificationType: PropTypes.oneOf(Object.keys(Color)), children: PropTypes.element, };
Уведомление
Компонент до сих пор имеет 2 реквизита:
цвет
: Строковое значение, которое определяет цвет фона уведомления и может быть либо информация, успех, предупреждение или ошибка.дети
: Любые элементы реагирования, которые мы хотим рендер внутри уведомления.
А вот его стили:
/* notify/Notification/Notification.module.css */ .notification { max-width: 430px; max-height: 200px; overflow: hidden; padding: 12px 48px 12px 12px; z-index: 99; font-weight: bold; position: relative; } .notification:not(:last-child) { margin-bottom: 8px; } .notification.info { background-color: #2196f3; } .notification.success { background-color: #4caf50; } .notification.warning { background-color: #ff9800; } .notification.error { background-color: #f44336; } .notification .closeButton { position: absolute; top: 12px; right: 12px; background: transparent; padding: 0; border: none; cursor: pointer; } .notification, .notification .closeButton { color: #fff; }
Наши уведомления должны быть представлены отдельно от структуры DOM приложения, используя их.
CreateContainer
Это функция помощника, которая создает элемент контейнера для уведомлений (если оно уже не существует) и добавить его непосредственно в тело документа:
// notify/createContainer/index.js import styles from "./container.module.css"; export default function createContainer() { const portalId = "notifyContainer"; let element = document.getElementById(portalId); if (element) { return element; } element = document.createElement("div"); element.setAttribute("id", portalId); element.className = styles.container; document.body.appendChild(element); return element; }
У этого есть Исправлено
позиция и размещена на вершине, в соответствии с нашими требованиями:
/* notify/createContainer/container.module.css */ .container { position: fixed; top: 16px; right: 16px; }
Затем мы можем использовать Reactom.CreatePortal
Чтобы сделать уведомление в контейнере, мы создаем:
// notify/Notification/index.js const container = createContainer(); export default function Notification({ color = Color.info, children }) { return createPortal({children}, container ); }
Первая демонстрация
Прежде чем написать демо, давайте разоблачать Уведомление
и его Цвет
объект в уведомлять/index.js
так что они могут быть импортированы и использованы:
// notify/index.js export { default as Notification, Color } from "./Notification";
Теперь давайте напишем демо для демонстрации различных уведомлений:
// App.js import React from "react"; import "./App.css"; import { Notification, Color } from "./notify"; function App() { const [notifications, setNotifications] = React.useState([]); const createNotification = (color) => setNotifications([...notifications, { color, id: notifications.length }]); return (); } export default App;Notification Demo
{notifications.map(({ id, color }) => (This is a notification! ))}
Наша демонстрация просто оказывает список уведомлений и имеет 4 разных кнопки для добавления цветных уведомлений в наш список.
Первая демонстрация: показывая основные цветные уведомления
Давайте позвольте закрыть уведомления, добавив OnDelete
опоры Уведомление
и сделать кнопку «Закрыть» вызвать эту функцию на клик:
// notify/Notification/index.js export default function Notification({ color = Color.info, onDelete, children, }) { return createPortal({children}, container ); }
Теперь в App.js
мы проходим OnDelete
Функция PROP, которая удаляет соответствующее уведомление из списка:
// App.js function App() { const [notifications, setNotifications] = React.useState([]); const createNotification = (color) => setNotifications([...notifications, { color, id: notifications.length }]); const deleteNotification = (id) => setNotifications( notifications.filter((notification) => notification.id !== id) ); return (); }Notification Demo
{notifications.map(({ id, color }) => (deleteNotification(id)} color={color} > This is a notification! ))}
Закрывающие уведомления
Уведомления добавляются и удаляются слишком быстро, что может запутать пользователей. Добавляя «Slide-In» и «выдвижные» анимации, мы делаем уведомления вести себя более естественным образом и улучшить пользовательский опыт.
Чтобы скользить уведомление, мы просто используем translatex
CSS преобразуется и переводит его от 100% до 0. Вот соответствующая анимация, созданная с ключевые кадры :
/* notify/Notification/Notification.module.css */ @keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(0%); } } .notification.slideIn { animation-name: slideIn; animation-duration: 0.3s; animation-timing-function: ease-in-out; }
«Swisk-Out» немного более сложнее. При ударе кнопки «Закрыть» необходимо иметь «закрытие» фазы перед вызовом OnDelete
функция опоры. Во время фазы закрытия мы можем сдвинуть уведомление, используя translatex (150%)
и добавить переход на Уведомление
сгладить «выдвижные».
Вот стили, соответствующие анимации «выдвижной»:
/* notify/Notification/Notification.module.css */ .notification { ... transition: transform 0.3s ease-out; } .notification.slideOut { transform: translateX(150%); flex: 0; }
Для достижения фазы закрытия в Уведомление
мы можем использовать логическую государственную переменную Исклеение
(установить на false
по умолчанию). Когда мы нажимаем на кнопку закрытия, мы устанавливаем Исклеение
к правда
, дождитесь продолжительности перехода ( 300 мс
здесь), а затем позвоните в OnDelete
функция.
Мы используем только Slidein
Стили анимации, когда мы не в фазе закрытия (то есть isclosing = false
) и Высказывание
Стили анимации, когда мы находимся в фазе закрытия (то есть Isloseing = True
).
// notify/Notification/index.js let timeToDelete = 300; export default function Notification({ color = Color.info, onDelete, children, }) { const [isClosing, setIsClosing] = React.useState(false); React.useEffect(() => { if (isClosing) { const timeoutId = setTimeout(onDelete, timeToDelete); return () => { clearTimeout(timeoutId); }; } }, [isClosing, onDelete]); return createPortal({children}, container ); }
Добавление «слайд-в» и «выдвижные» анимации
Когда уведомление удалено, те, которые под ним внезапно сдвигается наверху, чтобы заполнить его положение.
Чтобы сделать эту смену более естественным, давайте добавим контейнер вокруг уведомления, который сжимается гладко во время фазы закрытия:
// notify/Notification/index.js let timeToDelete = 300; export default function Notification({ color = Color.info, onDelete, children, }) { const [isClosing, setIsClosing] = React.useState(false); React.useEffect(() => { if (isClosing) { const timeoutId = setTimeout(onDelete, timeToDelete); return () => { clearTimeout(timeoutId); }; } }, [isClosing, onDelete]); return createPortal(, container ){children}
Контейнер имеет Макс-высота
200px
по умолчанию и сжимается на 0
во время фазы закрытия. Мы также должны переместить маржа
Определение контейнера:
/* notify/Notification/Notification.module.css */ .container { overflow: hidden; max-height: 200px; transition: max-height 0.3s ease-out; } .container:not(:last-child) { margin-bottom: 8px; } .container.shrink { max-height: 0; }
Анимационная смена уведомлений
Давайте добавим автотлозы
Boolean Prop для компонента уведомлений и использовать Useffect
Чтобы закрыть уведомление через 10 секунд, если опора установлено значение true.
// notify/Notification/index.js export default function Notification({ color = Color.info, autoClose = false, onDelete, children, }) { const [isClosing, setIsClosing] = React.useState(false); React.useEffect(() => { if (autoClose) { const timeoutId = setTimeout(() => setIsClosing(true), timeToClose); return () => { clearTimeout(timeoutId); }; } }, [autoClose]);
Теперь давайте изменим нашу демонстрацию, чтобы пройти Autoclose = True
к уведомлениям:
// App.js function App() { const [notifications, setNotifications] = React.useState([]); const createNotification = (color) => setNotifications([...notifications, { color, id: notifications.length }]); const deleteNotification = (id) => setNotifications( notifications.filter((notification) => notification.id !== id) ); return (); }Notification Demo
{notifications.map(({ id, color }) => (deleteNotification(id)} color={color} autoClose={true} > This is a notification! ))}
Теперь уведомления закрываются автоматически через 10 секунд их создания:
Создание уведомлений закрыть автоматически
Мы хотим иметь возможность неимнативно создавать уведомления, вызывая такие функции, как Успех ()
или Ошибка ()
Отказ
Хитрость состоит в том, чтобы создать компонент, аналогичный нашему Приложение
Тот, который отображается по умолчанию и предоставляет нам функцию создания уведомлений.
Давайте создадим УведомленияМаджер
Подавать этой цели:
// notify/NotificationsManager import React from "react"; import PropTypes from "prop-types"; import Notification from "./Notification"; export default function NotificationsManager({ setNotify }) { let [notifications, setNotifications] = React.useState([]); let createNotification = ({ color, autoClose, children }) => { setNotifications((prevNotifications) => { return [ ...prevNotifications, { children, color, autoClose, id: prevNotifications.length, }, ]; }); }; React.useEffect(() => { setNotify(({ color, autoClose, children }) => createNotification({ color, autoClose, children }) ); }, [setNotify]); let deleteNotification = (id) => { const filteredNotifications = notifications.filter( (_, index) => id !== index, [] ); setNotifications(filteredNotifications); }; return notifications.map(({ id, ...props }, index) => (deleteNotification(index)} {...props} /> )); } NotificationsManager.propTypes = { setNotify: PropTypes.func.isRequired, };
УведомленияManager получает один опора setnotify
, который используется для доступа к Createnotification
Функция для создания уведомлений неиспользуется.
Теперь давайте рендерем УведомленияМаджер
В том же контейнере, что и создайте наши функции создания уведомлений. Доступ к Createnotification
Функция через setnotify
Опыт и использовать его для создания наших функций создания уведомлений:
// notify/index.js import React from "react"; import ReactDOM from "react-dom"; import NotificationsManager from "./NotificationsManager"; import Notification, { Color } from "./Notification"; import createContainer from "./createContainer"; const containerElement = createContainer(); let notify; ReactDOM.render({ notify = notifyFn; }} />, containerElement ); export { Notification, Color }; export function info(children, autoClose) { return notify({ color: Color.info, children, autoClose, }); } export function success(children, autoClose) { return notify({ color: Color.success, children, autoClose, }); } export function warning(children, autoClose) { return notify({ color: Color.warning, children, autoClose, }); } export function error(children, autoClose) { return notify({ color: Color.error, children, autoClose, }); }
Теперь давайте проверим эти функции в App.js
Отказ Давайте также сделаем 2 изменения, чтобы улучшить нашу демонстрацию:
- Сделайте возможным показать как декларативные, так и императивные подходы.
- Использовать Реагистрационный свет Чтобы показать фрагмент кода для каждого подхода.
// App.js import React from "react"; import Highlight from "react-highlight"; import "./App.css"; import "./highlight-js-night-owl.css"; import { Notification, Color, info, success, warning, error } from "./notify"; const message = "This is a notification!"; function DeclarativeDemo() { const [notifications, setNotifications] = React.useState([]); const createNotification = (color) => setNotifications([...notifications, { color, id: notifications.length }]); const deleteNotification = (id) => setNotifications( notifications.filter((notification) => notification.id !== id) ); return ( <>{`const [notifications, setNotifications] = React.useState([]); const createNotification = (color) => setNotifications([...notifications, { color, id: notifications.length }]); const deleteNotification = (id) => setNotifications( notifications.filter((notification) => notification.id !== id) ); return ( <> {notifications.map(({ id, color }) => ( {notifications.map(({ id, color }) => (deleteNotification(id)} color={color} autoClose={true} > {message} ))} );`}deleteNotification(id)} color={color} autoClose={true} > {message} ))} ); } function ImperativeDemo() { return ( <>{`<> `} ); } function App() { const [demo, setDemo] = React.useState("declarative"); return ({demo === "declarative" ?); } export default App;: }
Примечание: Мы хотели бы помочь вам построить красивые и быстрые веб-приложения. Свяжитесь с нами Отказ
Оригинал: “https://dev.to/seif_ghezala/how-to-create-react-toasts-notifications-with-0-dependencies-1a9n”