Custom hooks для MUI

MUI (Material-UI) предоставляет мощный набор компонентов для построения интерфейсов, но для повышения повторного использования логики и упрощения работы с состоянием компонентов часто применяются custom hooks. Они позволяют инкапсулировать взаимодействие с MUI-компонентами, обработку событий, управление состояниями и интеграцию с темing-системой.


Создание простого custom hook для управления состоянием модального окна

Частой задачей является управление состоянием компонентов, таких как Dialog или Drawer. Вместо дублирования логики открытия/закрытия во множестве компонентов создается hook:

import { useState, useCallback } from 'react';

export function useModal() {
  const [isOpen, setIsOpen] = useState(false);

  const open = useCallback(() => setIsOpen(true), []);
  const close = useCallback(() => setIsOpen(false), []);
  const toggle = useCallback(() => setIsOpen(prev => !prev), []);

  return { isOpen, open, close, toggle };
}

Применение в компоненте с MUI Dialog:

import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
import { useModal } from './hooks/useModal';

function MyDialogComponent() {
  const { isOpen, open, close } = useModal();

  return (
    <>
      <Button variant="contained" onCl ick={open}>Открыть диалог</Button>
      <Dialog open={isOpen} onCl ose={close}>
        <DialogTitle>Пример диалога</DialogTitle>
        <DialogContent>Содержимое диалога</DialogContent>
        <DialogActions>
          <Button onCl ick={close}>Закрыть</Button>
        </DialogActions>
      </Dialog>
    </>
  );
}

Ключевая особенность: логика состояния вынесена в hook, что облегчает тестирование и повторное использование.


Hook для работы с формами MUI TextField

MUI предоставляет контролируемые компоненты ввода (TextField, Select, Checkbox). Частая задача — централизованное управление значениями и ошибками формы. Создание hook для этого упрощает обработку:

import { useState, useCallback } from 'react';

export function useForm(initialValues) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});

  const handleChange = useCallback((field) => (event) => {
    setValues(prev => ({ ...prev, [field]: event.target.value }));
  }, []);

  const setError = useCallback((field, message) => {
    setErrors(prev => ({ ...prev, [field]: message }));
  }, []);

  const reset = useCallback(() => {
    setValues(initialValues);
    setErrors({});
  }, [initialValues]);

  return { values, errors, handleChange, setError, reset };
}

Применение в компоненте с MUI:

import { TextField, Button } from '@mui/material';
import { useForm } from './hooks/useForm';

function LoginForm() {
  const { values, errors, handleChange, setError, reset } = useForm({ email: '', password: '' });

  const handleSubmit = () => {
    if (!values.email) setError('email', 'Email обязателен');
    if (!values.password) setError('password', 'Пароль обязателен');
  };

  return (
    <form>
      <TextField
        label="Email"
        value={values.email}
        onCha nge={handleChange('email')}
        error={!!errors.email}
        helperText={errors.email}
      />
      <TextField
        label="Пароль"
        type="password"
        value={values.password}
        onCha nge={handleChange('password')}
        error={!!errors.password}
        helperText={errors.password}
      />
      <Button variant="contained" onCl ick={handleSubmit}>Войти</Button>
      <Button variant="outlined" onCl ick={reset}>Сбросить</Button>
    </form>
  );
}

Преимущество: hook изолирует состояние и валидацию, позволяя переиспользовать его для разных форм.


Hook для динамического управления темой MUI

MUI поддерживает тему через ThemeProvider. Custom hook позволяет менять тему динамически:

import { useState, useMemo, useCallback } from 'react';
import { createTheme } from '@mui/material/styles';

export function useThemeSwitcher() {
  const [mode, setMode] = useState('light');

  const toggleMode = useCallback(() => {
    setMode(prev => (prev === 'light' ? 'dark' : 'light'));
  }, []);

  const theme = useMemo(() => createTheme({ palette: { mode } }), [mode]);

  return { theme, mode, toggleMode };
}

Применение с ThemeProvider:

import { ThemeProvider, Button } from '@mui/material';
import { useThemeSwitcher } from './hooks/useThemeSwitcher';

function App() {
  const { theme, toggleMode, mode } = useThemeSwitcher();

  return (
    <ThemeProvider theme={theme}>
      <Button onCl ick={toggleMode}>
        Переключить на {mode === 'light' ? 'темную' : 'светлую'} тему
      </Button>
      {/* Остальные компоненты */}
    </ThemeProvider>
  );
}

Преимущество: изменения темы становятся простыми, без повторного создания компонентов.


Hook для управления таблицами MUI DataGrid

DataGrid часто требует обработки сортировки, фильтров и пагинации. Создание hook позволяет инкапсулировать эти состояния:

import { useState, useCallback } from 'react';

export function useDataGrid(initialRows = [], initialPageSize = 10) {
  const [rows, setRows] = useState(initialRows);
  const [pageSize, setPageSize] = useState(initialPageSize);
  const [sortModel, setSortModel] = useState([]);
  const [filterModel, setFilterModel] = useState({ items: [] });

  return {
    rows,
    setRows,
    pageSize,
    setPageSize,
    sortModel,
    setSortModel,
    filterModel,
    setFilterModel,
  };
}

Применение в компоненте:

import { DataGrid } from '@mui/x-data-grid';
import { useDataGrid } from './hooks/useDataGrid';

function UsersTable({ users }) {
  const { rows, setRows, pageSize, setPageSize, sortModel, setSortModel } = useDataGrid(users);

  return (
    <DataGrid
      rows={rows}
      columns={[{ field: 'id' }, { field: 'name' }]}
      pageSize={pageSize}
      onPageSizeCha nge={setPageSize}
      sortingOrder={['asc', 'desc']}
      sortModel={sortModel}
      onSortModelCha nge={setSortModel}
    />
  );
}

Результат: логика управления таблицей полностью вынесена в hook, обеспечивая чистоту компонентов.


Рекомендации по созданию custom hooks с MUI

  1. Изоляция состояния: любой hook должен управлять только одной логической областью (формы, модальные окна, таблицы).
  2. Повторное использование: общее поведение компонентов выносится в hook для уменьшения дублирования кода.
  3. Интеграция с MUI API: hooks могут напрямую использовать функции MUI (useTheme, useMediaQuery, useAutocomplete).
  4. Оптимизация производительности: применение useCallback и useMemo для предотвращения лишних ререндеров.
  5. Совместимость с TypeScript: при необходимости типизация значений повышает безопасность и предсказуемость поведения.

Эти подходы позволяют создавать чистые, масштабируемые и легко тестируемые интерфейсы, где MUI выполняет роль визуального каркаса, а custom hooks инкапсулируют всю логику взаимодействия.