Создание CRUD-приложения с помощью Material UI и Strapi


Введение

Мы создадим панель администратора, которая поддерживает CRUD-операции, имеет встроенную аутентификацию и функцию режима мутации, используя лучшие инструменты промышленного стандарта.

Стандартные инструменты и практики могут быть труднодоступными и отнимать много времени на самостоятельную поддержку. Фреймворки могут сэкономить ваше время, выполняя эту работу за вас. Итак, мы будем использовать мощные фреймворки, включая Material UI, Strapi и refine, чтобы создать высококачественную панель администратора.

Разработка пользовательского интерфейса может быть сложным и трудоемким процессом, но такой инструмент, как Material UI, может упростить этот процесс и ускорить цикл разработки. В этом руководстве мы будем использовать преимущества Material UI и встроенные крючки refine для обработки получения данных и мутаций. Мы также интегрируем поставщик данных Strapi, который поддерживается refine.

Мы рассмотрим процесс создания списка, создания и удаления постов в приложении refine и воспользуемся компонентами и крючками refine для создания нашей функциональности.

Мы рассмотрим следующие шаги:

  • В чем преимущества использования refine?
  • Загрузка приложения refine
    • Реализация поставщика данных Strapi v4
  • CRUD-операции
    • Вывод записей в список
    • Добавление ресурсов и страниц подключения к приложению refine
    • Работа с реляционными данными
    • Создание записи
    • Редактирование записи
    • Удаление записи
  • Реализация режима мутации
  • Совместное использование текущей страницы с фильтрами
  • Заключение
  • Живой пример StackBlitz

Предварительные условия

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

  • уточнить
  • уточнить поставщик данных StrapiV4
  • материальный пользовательский интерфейс
  • refine Учебник по материальному пользовательскому интерфейсу

Версия вашей ноды должна быть минимальной v16.14.0.

Каковы преимущества использования refine?

refine — это внутренний инструментальный фреймворк React без головы, который поможет вам быстро разрабатывать приложения для B2B и B2C. Он ускоряет работу и при этом позволяет полностью настраивать приложения, что делает его идеальным выбором для быстрой разработки с профессиональными функциями.

  • Он является открытым исходным кодом по лицензии MIT.
  • Он прост в использовании и изучении. Существует множество примеров, которые помогут вам начать работу, а также документация.
  • Это фреймворк, который не требует использования каких-либо библиотек или фреймворков пользовательского интерфейса.
  • Поддерживает Ant Design и Material UI нативно для быстрых и простых решений. Благодаря безголовому подходу вы можете легко интегрировать свое собственное решение пользовательского интерфейса.
  • Не зависит от бэкенда, поэтому вы можете подключиться к любому бэкенду, который вам нужен.
  • Настраиваемость, что означает, что вы можете изменить его в соответствии с вашими потребностями.
  • Некоторые из основных возможностей: выборка данных и управление состоянием, маршрутизация, аутентификация, авторизация, интернационализация, реальное время, режимы мутации с оптимистичным и пессимистичным режимами, а также неисполнимый режим.

Загрузка приложения refine

Мы будем использовать мастер superplate CLI для создания и настройки приложения refine.

Выполните следующую команду

npx superplate-cli -p refine-react material-ui-example
Войти в полноэкранный режим Выйти из полноэкранного режима

Выберите следующие опции для завершения работы мастера CLI:

? Do you want to use a UI Framework?:
❯ Material UI

? Do you want an extended theme?:
❯ No

? Do you want to add dark mode support?:
❯ No

? Router Provider:
❯ React Router v6

? Data Provider:
❯ Strapi v4

? Do you want a customized layout?
❯ No

? i18n - Internationalization:
❯ No
Войти в полноэкранный режим Выйти из полноэкранного режима

CLI должен создать проект и установить выбранные зависимости.

Реализация провайдера данных Strapi v4

Провайдеры данных — это усовершенствованные крючки, позволяющие удобно использовать различные API и сервисы данных.
Необходимые настройки провайдера данных Strapi добавляются автоматически мастером CLI.

Чтобы использовать Fake Strapi API, нам нужно изменить API URL в папке проекта.

// src/constants.ts
export const API_URL = "https://api.strapi-v4.refine.dev";
Вход в полноэкранный режим Выход из полноэкранного режима

Обратитесь к документации refine для получения более подробной информации о поддержке refine Strapi V4→.

Обратитесь к документации поставщика данных refine для получения подробной информации→

Обратитесь к официальной документации Strapi v4→

CRUD-операции

Мы собираемся реализовать функции CRUD-операций, такие как листинг, создание и редактирование записей.

Создание списка записей

Нам нужно создать страницу PostList для отображения данных в пользовательском интерфейсе.

Во-первых, нам понадобится интерфейс для работы с данными из конечной точки API.

Мы создадим новую папку interfaces в /src, если у вас ее еще нет. Затем создадим файл index.d.ts со следующим кодом:

// src/interfaces/index.d.ts
export interface ICategory {
    id: number;
    title: string;
}

export interface IPost {
    id: number;
    title: string;
    content: string;
    status: "published" | "draft" | "rejected";
    category: ICategory;
    createdAt: string;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь создадим новую папку pages/posts в /src. В этой папке создадим файл list.tsx со следующим кодом:

// src/pages/posts/list.tsx
import React from "react";
import {
    useDataGrid,
    DataGrid,
    GridColumns,
    DateField,
    List,
} from "@pankod/refine-mui";

import { IPost } from "interfaces";

export const PostList: React.FC = () => {
    const { dataGridProps } = useDataGrid<IPost>();

    const columns = React.useMemo<GridColumns<IPost>>(
        () => [
            { field: "title", headerName: "Title", flex: 1, minWidth: 350 },
            {
                field: "createdAt",
                headerName: "CreatedAt",
                minWidth: 220,
                renderCell: function render({row}) {
                    return (
                        <DateField format="LLL" value={row.createdAt} />
                    );
                },
            }
        ],
        [],
    );

    return (
        <List>
            <DataGrid {...dataGridProps} columns={columns} autoHeight />
        </List>
    );
};
Вход в полноэкранный режим Выход из полноэкранного режима

Мы импортируем и используем компоненты Material UI из папки refine @pankod/refine-mui для отображения данных.

<DataGrid/> — это родной компонент Material UI. Он отображает записи строка за строкой в виде таблицы. <DataGrid/> ожидает переменную columns как обязательную.

refine hook useDataGrid получает данные из API и оборачивает их различными вспомогательными хуками, необходимыми для компонента <DataGrid/>. Функции взаимодействия с данными, такие как сортировка, фильтрация и пагинация, будут мгновенно доступны на <DataGrid/> с помощью этой единственной строки кода.

Для получения дополнительной информации обратитесь к документации по хуку useDataGrid в refine→.

Массив columns используется для отображения и форматирования каждого поля, отображаемого на <DataGrid/> полевой prop сопоставляет поле с соответствующим ключом из ответа API. Реквизит renderCell используется для выбора соответствующего компонента Field для данного типа данных.

Информация: «Хук useDataGrid работает совместимо как с компонентом <DataGrid>, так и с компонентом <DataGridPro>«.

Обратите внимание, что вам понадобится файл src/App.tsx, чтобы найти ваши страницы и посты. В папку /pages поместите этот файл index.tsx, который позволяет использовать все, что находится в папке posts, в других местах.

// src/pages/posts/index.tsx
export * from "./list";
Вход в полноэкранный режим Выход из полноэкранного режима

Подробные объяснения и примеры смотрите в официальном руководстве refine’s Material UI tutorial→.

Добавление ресурсов и страниц подключения в приложение refine

Теперь мы готовы начать подключение к нашему API, добавив ресурс в наше приложение.
Мы добавим конечную точку /posts/ из нашего примера API в качестве ресурса.

Мы добавим выделенный код в наш App.tsx для подключения к конечной точке и странице List.

// App.tsx
import { Refine } from "@pankod/refine-core";
import {
    notificationProvider,
    RefineSnackbarProvider,
    CssBaseline,
    GlobalStyles,
    Layout,
    ThemeProvider,
    LightTheme,
    ReadyPage,
    ErrorComponent,
} from "@pankod/refine-mui";
import routerProvider from "@pankod/refine-react-router-v6";
import { DataProvider } from "@pankod/refine-strapi-v4";

import { authProvider, axiosInstance } from "./authProvider";
import { API_URL } from "./constants";
// ====>
import { PostList } from "./pages/posts";
// <====

function App() {
    return (
        <ThemeProvider theme={LightTheme}>
            <CssBaseline />
            <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
            <RefineSnackbarProvider>
                <Refine
                    notificationProvider={notificationProvider}
                    Layout={Layout}
                    ReadyPage={ReadyPage}
                    catchAll={<ErrorComponent />}
                    routerProvider={routerProvider}
                    authProvider={authProvider}
                    dataProvider={DataProvider(API_URL + `/api`, axiosInstance)}
                    // ====>
                    resources={[
                        {
                            name: "posts",
                            list: PostList,
                        },
                    ]}
                    // <====
                />
            </RefineSnackbarProvider>
        </ThemeProvider>
    );
}

export default App;

Вход в полноэкранный режим Выход из полноэкранного режима

Информация: resources — это свойство <Refine/>, представляющее конечные точки API. Свойство name каждого отдельного ресурса должно соответствовать одной из конечных точек вашего API!

После завершения настройки перейдите в папку проекта и запустите проект:

npm run dev
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь приложение должно перенаправить вас на URL, определенный свойством name.

Оно попросит вас войти в приложение. Попробуйте использовать эти учетные данные:

Имя пользователя: demo@refine.dev

Пароль: demodemo

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

Работа с реляционными данными

Отношения не заполняются при выборке записей. Мы будем использовать опцию metaData для использования реляционного заполнения для Strapi v4 API.

Записи из конечной точки /posts, которые имели поле id категории. Чтобы автоматически получить названия категорий из конечной точки /categories для каждой записи и показать в нашей таблице, нам нужно использовать функцию populate в Strapi v4.

Мы установим параметр populate, чтобы определить, какие поля будут заполнены.

// src/pages/post/list.tsx
  const { dataGridProps } = useDataGrid<IPost>({
        // ====>
        metaData: {
            populate: ["category"],
        },
        // <====
    });

Вход в полноэкранный режим Выход из полноэкранного режима

Чтобы показать поле категории в таблице, нам нужно добавить новую колонку в компонент PostList.

// src/pages/post/list.tsx
  const columns = React.useMemo<GridColumns<IPost>>(
        () => [
           ...
           // ====>
            {
                field: "category.title",
                headerName: "Category",
                minWidth: 250,
                flex: 1,
                renderCell: function render({ row }) {
                    return row.category?.title;
                },
            },
           // <====
           ...
        ],
        [],
    );

Вход в полноэкранный режим Выход из полноэкранного режима

Совет: Мы используем преимущества функции реляционного заполнения Strapi V4 с помощью параметра populate. Он позволяет автоматически получать реляционные данные.

Если вы используете другой REST API, в котором реляционные популяции необходимо обрабатывать вручную, вы можете посмотреть пример по ссылке→.

Для получения дополнительной информации обратитесь к документации по refine Strapi v4.

Создание записи

Material UI предоставляет уже стилизованные, но все еще очень настраиваемые входы, которые инкапсулируют добавление меток и обработку ошибок с помощью вспомогательных текстов. Однако нам нужна сторонняя библиотека для работы с формами при использовании Material UI. React Hook Form — один из лучших вариантов для этой работы!

Библиотека React Hook Form была интегрирована в refine (@pankod/refine-react-hook-form). Это означает, что теперь вы можете использовать Material UI для своих форм и управлять ими с помощью @pankod/refine-react-hook-form.

Сначала мы создадим страницу PostCreate для создания новых записей.

// src/pages/posts/create
import {
    Box,
    TextField,
    Autocomplete,
    useAutocomplete,
    Create,
} from "@pankod/refine-mui";
import { useForm, Controller } from "@pankod/refine-react-hook-form";

import { ICategory } from "interfaces";

export const PostCreate: React.FC = () => {
    const {
        refineCore: { formLoading },
        saveButtonProps,
        register,
        control,
        formState: { errors },
    } = useForm();

    const { autocompleteProps } = useAutocomplete<ICategory>({
        resource: "categories",
    });

    return (
        <Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
            <Box
                component="form"
                sx={{ display: "flex", flexDirection: "column" }}
                autoComplete="off"
            >
                <TextField
                    {...register("title", { required: "Title is required" })}
                    error={!!errors?.title}
                    helperText={errors.title?.message}
                    margin="normal"
                    required
                    fullWidth
                    id="title"
                    label="Title"
                    name="title"
                    autoFocus
                />
                <Controller
                    control={control}
                    name="category"
                    rules={{ required: "Category is required" }}
                    render={({ field }) => (
                        <Autocomplete
                            {...autocompleteProps}
                            {...field}
                            onChange={(_, value) => {
                                field.onChange(value);
                            }}
                            getOptionLabel={(item) => {
                                return item.title ? item.title : "";
                            }}
                            isOptionEqualToValue={(option, value) =>
                                value === undefined || option.id === value.id
                            }
                            renderInput={(params) => (
                                <TextField
                                    {...params}
                                    label="Category"
                                    margin="normal"
                                    variant="outlined"
                                    error={!!errors.category}
                                    helperText={errors.category?.message}
                                    required
                                />
                            )}
                        />
                    )}
                />
            </Box>
        </Create>
    );
};


Вход в полноэкранный режим Выйдите из полноэкранного режима

Добавьте экспорт компонента в index.tsx.

// src/pages/posts/index.tsx
export * from "./create";
Войти в полноэкранный режим Выход из полноэкранного режима

После создания компонента <PostCreate> добавьте его в ресурс с помощью реквизита create:

// App.tsx
...

import {
    PostList, 
 // ====>
    PostCreate,
 // <====
} from "pages/posts";

...

const App: React.FC = () => {
    return (
        <ThemeProvider theme={LightTheme}>
            <CssBaseline />
            <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
            <RefineSnackbarProvider>
                <Refine
                    authProvider={authProvider}
                    routerProvider={routerProvider}
                    dataProvider={dataProvider(API_URL)}
                    notificationProvider={notificationProvider}
                    ReadyPage={ReadyPage}
                    Layout={Layout}
                    catchAll={<ErrorComponent />}
                    resources={[
                        {
                            name: "posts",
                            list: PostList,
                            // ===>
                            create: PostCreate,
                            // <===
                        },
                    ]}
                />
            </RefineSnackbarProvider>
        </ThemeProvider>
    );
};
Войти в полноэкранный режим Выйти из полноэкранного режима

Попробуйте это в браузере и посмотрите, сможете ли вы создавать новые посты с нуля.

Редактирование записи

Начнем с создания новой страницы <PostEdit>, отвечающей за редактирование существующей одной записи:

// src/pages/posts/edit.tsx
import { Controller, useForm } from "@pankod/refine-react-hook-form";
import {
    Edit,
    Box,
    TextField,
    Autocomplete,
    useAutocomplete,
} from "@pankod/refine-mui";

import { ICategory } from "interfaces";

export const PostEdit: React.FC = () => {
    const {
        refineCore: { formLoading },
        saveButtonProps,
        register,
        control,
        formState: { errors },
    } = useForm({ refineCoreProps: { metaData: { populate: ["category"] } } });

    const { autocompleteProps } = useAutocomplete<ICategory>({
        resource: "categories",
        defaultValue: queryResult?.data?.data.category.id,
        queryOptions: { enabled: !!queryResult?.data?.data.category.id },
    });

    return (
        <Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
            <Box
                component="form"
                sx={{ display: "flex", flexDirection: "column" }}
                autoComplete="off"
            >
                <TextField
                    {...register("title", { required: "Title is required" })}
                    error={!!errors?.title}
                    helperText={errors.title?.message}
                    margin="normal"
                    required
                    fullWidth
                    id="title"
                    label="Title"
                    name="title"
                    defaultValue={" "}
                    autoFocus
                />
                <Controller
                    control={control}
                    name="category"
                    rules={{ required: "Category is required" }}
                    defaultValue=""
                    render={({ field }) => (
                        <Autocomplete
                            {...autocompleteProps}
                            {...field}
                            onChange={(_, value) => {
                                field.onChange(value);
                            }}
                            getOptionLabel={(item) => {
                                return item.title
                                    ? item.title
                                    : autocompleteProps?.options?.find(
                                          (p) =>
                                              p.id.toString() ===
                                              item.toString(),
                                      )?.title ?? "";
                            }}
                            isOptionEqualToValue={(option, value) =>
                                value === undefined ||
                                option.id.toString() === value.toString()
                            }
                            renderInput={(params) => (
                                <TextField
                                    {...params}
                                    label="Category"
                                    margin="normal"
                                    variant="outlined"
                                    error={!!errors.category}
                                    helperText={errors.category?.message}
                                    required
                                />
                            )}
                        />
                    )}
                />
            </Box>
        </Edit>
    );
};
Вход в полноэкранный режим Выход из полноэкранного режима

Добавьте экспорт компонента в index.tsx.

// src/pages/posts/index.tsx
export * from "./edit";
Войдите в полноэкранный режим Выход из полноэкранного режима

Мы добавим кнопку редактирования к каждой строке списка, определив колонку Actions на странице PostList.

// src/pages/posts/list.tsx
import React from "react";
import {
    useDataGrid,
    DataGrid,
    GridColumns,
    DateField,
    List,
    // ====>
    Stack,
    EditButton,
    // <====
} from "@pankod/refine-mui";

import { IPost } from "interfaces";

export const PostList: React.FC = () => {
    const { dataGridProps } = useDataGrid<IPost>({
        metaData: {
            populate: ["category"],
        },
    });

    const columns = React.useMemo<GridColumns<IPost>>(
        () => [
            { field: "title", headerName: "Title", flex: 1, minWidth: 350 },
            {
                field: "category.title",
                headerName: "Category",
                minWidth: 250,
                flex: 1,
                renderCell: function render({ row }) {
                    return row.category?.title;
                },
            },

            {
                field: "createdAt",
                headerName: "CreatedAt",
                minWidth: 220,
                renderCell: function render({ row }) {
                    return <DateField format="LLL" value={row.createdAt} />;
                },
            },
             // ====>
            {
                headerName: "Actions",
                headerAlign: "center",
                field: "actions",
                minWidth: 180,
                align: "center",
                flex: 1,
                sortable: false,
                renderCell: function render({ row }) {
                    return (
                        <Stack direction="row" spacing={1}>
                            <EditButton
                                size="small"
                                hideText
                                recordItemId={row.id}
                            />
                        </Stack>
                    );
                },
            },
           // <====
        ],
        [],
    );

    return (
        <List>
            <DataGrid {...dataGridProps} columns={columns} autoHeight />
        </List>
    );
};
Вход в полноэкранный режим Выход из полноэкранного режима

После создания компонента <PostEdit> добавьте его в ресурс с реквизитом edit:

// App.tsx
...

import {
    PostList,
    PostCreate,
 // ====>
    PostEdit
 // <====
} from "pages/posts";

...

const App: React.FC = () => {
    return (
        <ThemeProvider theme={LightTheme}>
            <CssBaseline />
            <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
            <RefineSnackbarProvider>
                <Refine
                    authProvider={authProvider}
                    routerProvider={routerProvider}
                    dataProvider={dataProvider(API_URL)}
                    notificationProvider={notificationProvider}
                    ReadyPage={ReadyPage}
                    Layout={Layout}
                    catchAll={<ErrorComponent />}
                    resources={[
                        {
                            name: "posts",
                            list: PostList,
                            create: PostCreate,
                            // ====>
                            edit: PostEdit
                            // <====
                        },
                    ]}
                />
            </RefineSnackbarProvider>
        </ThemeProvider>
    );
};
Войти в полноэкранный режим Выйти из полноэкранного режима

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

Удаление записи

Удаление записи может быть выполнено двумя способами.

Первый способ — добавить кнопку удаления в каждую строку, поскольку refine не добавляет ее автоматически, поэтому мы должны обновить наш компонент <PostList>, чтобы добавить <DeleteButton> для каждой записи.

Мы добавим новую ячейку в столбец Actions для отображения кнопки удаления в каждой строке.

// src/pages/list.tsx
import React from "react";
import {
    useDataGrid,
    DataGrid,
    GridColumns,
    EditButton,
    DateField,
    List,
    Stack,
 // ====>
    DeleteButton,
 // <====
} from "@pankod/refine-mui";

import { IPost } from "interfaces";

export const PostList: React.FC = () => {
    const { dataGridProps } = useDataGrid<IPost>({
        metaData: {
            populate: ["category"],
        },
    });


    const columns = React.useMemo<GridColumns<IPost>>(
      ...

            {
                headerName: "Actions",
                headerAlign: "center",
                field: "actions",
                minWidth: 180,
                align: "center",
                flex: 1,
                sortable: false,
                renderCell: function render({ row }) {
                    return (
                        <Stack direction="row" spacing={1}>
                            <EditButton
                                size="small"
                                hideText
                                recordItemId={row.id}
                            />
                             // ====>
                            <DeleteButton
                                size="small"
                                hideText
                                recordItemId={row.id}
                            />
                             // <====

                        </Stack>
                    );
                },
            },
        ],
        [],
    );

    return (
        <List>
            <DataGrid {...dataGridProps} columns={columns} autoHeight />
        </List>
    );
};
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь мы можем удалить запись, нажав на кнопку удаления и подтвердив ее.

Второй способ — показать кнопку удаления на странице <PostEdit>. Чтобы показать кнопку удаления на странице редактирования, объекту ресурса необходимо передать параметр canDelete.

// App.tsx
... 

function App() {
    return (
        <ThemeProvider theme={LightTheme}>
            <CssBaseline />
            <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
            <RefineSnackbarProvider>
                <Refine
                    notificationProvider={notificationProvider}
                    Layout={Layout}
                    ReadyPage={ReadyPage}
                    catchAll={<ErrorComponent />}
                    routerProvider={routerProvider}
                    authProvider={authProvider}
                    dataProvider={DataProvider(API_URL + `/api`, axiosInstance)}
                    resources={[
                        {
                            name: "posts",
                            list: PostList,
                            create: PostCreate,
                            edit: PostEdit,
                             // ====>
                            canDelete: true,
                             // <====
                        },
                    ]}
                />
            </RefineSnackbarProvider>
        </ThemeProvider>
    );
}

export default App;


Вход в полноэкранный режим Выход из полноэкранного режима

Кнопка <DeleteButton> должна отображаться в форме редактирования.

Реализация режима мутации

Мы хотим показать, как режимы мутации делают ваше приложение более отзывчивым к пользователю. Refine предлагает три режима для мутаций, которые называются pessimistic, optimistic и undoable. Эти режимы определяют, когда будут выполняться побочные эффекты.

Если кратко описать:

pessimistic: Обновление пользовательского интерфейса задерживается до тех пор, пока мутация не будет подтверждена сервером.

optimistic: Обновления пользовательского интерфейса немедленно обновляются до подтверждения сервером.

undoable: Обновления пользовательского интерфейса обновляются немедленно, но вы можете отменить мутацию.

Мы реализуем режим мутации undoable. Мутация применяется локально, перенаправление и обновления пользовательского интерфейса выполняются немедленно, как если бы мутация была успешной. Ожидание настраиваемого периода тайм-аута перед применением мутации.

Во время тайм-аута мутация может быть отменена из уведомления с помощью кнопки отмены, и пользовательский интерфейс вернется обратно соответствующим образом.

Обратитесь к документации Refine mutation mode для получения более подробной информации→.

Чтобы активировать режим мутации, установим свойство mutationMode для компонента <Refine/>.

// App.tsx
...
function App() {
    return (
        <ThemeProvider theme={LightTheme}>
            <CssBaseline />
            <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
            <RefineSnackbarProvider>
                <Refine
                    notificationProvider={notificationProvider}
                    Layout={Layout}
                    ReadyPage={ReadyPage}
                    catchAll={<ErrorComponent />}
                    routerProvider={routerProvider}
                    authProvider={authProvider}
                    dataProvider={DataProvider(API_URL + `/api`, axiosInstance)}
                    resources={[
                        {
                            name: "posts",
                            list: PostList,
                            create: PostCreate,
                            edit: PostEdit,
                            canDelete: true,
                        },
                    ]}
                     // ====>
                    mutationMode="undoable"
                     // <====
                />
            </RefineSnackbarProvider>
        </ThemeProvider>
    );
}

export default App;
Вход в полноэкранный режим Выход из полноэкранного режима

Совет: По умолчанию период тайм-аута установлен на 5000 мс. Вы можете изменить его, установив свойство undoableTimeout для компонента <Refine>.

Совместное использование текущей страницы с фильтрами

Представьте, что нам нужно поделиться текущей страницей с параметрами фильтрации и сортировки с нашими коллегами. Правильный способ сделать это — поделиться URL, включающим все необходимые параметры, такие как:

/posts?current=1&pageSize=8&sort[]=createdAt&order[]=desc
Войти в полноэкранный режим Выйти из полноэкранного режима

Refine предлагает свойство syncWithLocation, которое позволяет нам редактировать параметры запроса вручную и легко делиться текущей страницей, количеством элементов на странице, параметрами сортировки и фильтрации с другими.

// App.tsx
...
function App() {
    return (
        <ThemeProvider theme={LightTheme}>
            <CssBaseline />
            <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
            <RefineSnackbarProvider>
                <Refine
                    ...
                    mutationMode="undoable"
                    // ====>
                    syncWithLocation
                    // <====
                />
            </RefineSnackbarProvider>
        </ThemeProvider>
    );
}

export default App;
Вход в полноэкранный режим Выход из полноэкранного режима

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

Заключение

В этой статье мы покажем вам, как построить CRUD-панель администратора, используя refine и Material UI. Этот подход позволит вам быстро создать интерфейс администратора для вашего приложения с минимальным кодированием. Мы начнем с настройки нашего проекта с необходимыми зависимостями. Затем мы создадим наши компоненты CRUD, используя Material UI. Наконец, мы подключим все и добавим некоторые дополнительные функции из refine, например, режим мутации.

Мы рассмотрели:

  • Как создать приложение refine на основе bootstrap
  • Подключение провайдера данных Strapi v4 к приложению refine.
  • Создание страниц для операций CRUD
  • Реализация некоторых функций refine, таких как режим мутации и синхронизация местоположения.

refine — это инструмент с открытым исходным кодом, позволяющий быстро и гибко разрабатывать CRUD-панели администратора или веб-приложения. С ним легко начать работу, и он не требует большого количества кода. У него хорошая документация, в которой собраны примеры, рекомендации и учебники по использованию лучших практик. refine постоянно обновляется, добавляя новые функции и улучшения.

Для получения дополнительной информации обратитесь к официальной странице refine→.

Пример StackBlitz в реальном времени

Имя пользователя: demo@refine.dev

Пароль: demodemo

Создавайте CRUD-приложения на основе React без ограничений

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

Обратите внимание на refine, если вас интересует безголовый фреймворк, который можно использовать с любым пользовательским дизайном или UI-Kit для 100% контроля над стилизацией.

refine — это основанный на React фреймворк для создания CRUD-приложений без ограничений.
Он может ускорить время разработки до 3 раз без ущерба для свободы стиля, кастомизации и рабочего процесса проекта.

refine является безголовым по своей конструкции и подключает 30+ бэкенд-сервисов «из коробки», включая пользовательские REST и GraphQL API.

Посетите GitHub-репозиторий refine для получения дополнительной информации, демонстраций, учебников и примеров проектов.

Оцените статью
devanswers.ru
Добавить комментарий