Hello world!
Zustand (читается как "цуштанд", что переводится с немецкого как "состояние") — это, на мой взгляд, один из лучших на сегодняшний день инструментов для управления состоянием приложений, написанных на React.
В этой статье я немного расскажу о том, как он работает.
Давайте начнем с примера использования zustand
для реализации функционала отображения/скрытия модального окна.
Код проекта лежит здесь.
Демо:
Для работы с зависимостями я буду использовать Yarn.
Создаем шаблон React-приложения с помощью Vite:
# zustand-test - название приложения
# react-ts - шаблон проекта, в данном случае React
yarn create vite zustand-test --template react
Переходим в созданную директорию, устанавливаем основные зависимости и запускаем сервер для разработки:
cd zustand-test
yarn
yarn dev
Устанавливаем дополнительные зависимости:
yarn add zustand use-sync-external-store react-use
- react-use — большая коллекция кастомных хуков;
- use-sync-external-store — об этом мы поговорим чуть позже.
Определяем хук для управления состоянием модалки в файле hooks/useModal.js
:
import { create } from 'zustand'
const useModal = create((set) => ({
isOpen: false,
open: () => set({ isOpen: true }),
close: () => set({ isOpen: false }),
}))
export default useModal
Определяем компонент модалки в файле components/Modal.jsx
:
import { useEffect, useRef } from 'react'
import { useClickAway } from 'react-use'
import useModal from '../hooks/useModal'
export default function Modal() {
// состояние модалки
const modal = useModal()
// ссылка на модалку
const modalRef = useRef(null)
// ссылка на содержимое модалки
const modalContentRef = useRef(null)
useEffect(() => {
if (!modalRef.current) return
// показываем/скрываем модалку в зависимости от значения индикатора `isOpen`
// `showModal` и `close` - это нативные методы, предоставляемые HTML-элементом `dialog`
if (modal.isOpen) {
modalRef.current.showModal()
} else {
modalRef.current.close()
}
}, [modal.isOpen])
// скрываем модалку при клике за пределами ее содержимого
useClickAway(modalContentRef, modal.close)
if (!modal.isOpen) return null
return (
<dialog
style={{
padding: 0,
}}
ref={modalRef}
>
<div
style={{
padding: '1rem',
display: 'flex',
alignItems: 'center',
gap: '1rem',
}}
ref={modalContentRef}
>
<div>Modal content</div>
<button onClick={modal.close}>X</button>
</div>
</dialog>
)
}
Определяем минимальные стили в файле index.css
:
body {
margin: 0;
}
#root {
min-height: 100vh;
display: grid;
place-content: center;
}
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.4);
}
Наконец, рендерим модалку в файле App.jsx
:
import Modal from './components/Modal'
import useModal from './hooks/useModal'
function App() {
const modal = useModal()
return (
<>
<button onClick={modal.open}>Open modal</button>
<Modal />
</>
)
}
export default App
Это было легко, не правда ли? А все благодаря магии функции create