- Введение
- Предварительные условия
- Каковы преимущества использования refine?
- Загрузка приложения refine
- Реализация провайдера данных Strapi v4
- CRUD-операции
- Создание списка записей
- Добавление ресурсов и страниц подключения в приложение refine
- Работа с реляционными данными
- Создание записи
- Редактирование записи
- Удаление записи
- Реализация режима мутации
- Совместное использование текущей страницы с фильтрами
- Заключение
- Пример StackBlitz в реальном времени
- Создавайте CRUD-приложения на основе React без ограничений
Введение
Мы создадим панель администратора, которая поддерживает 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 для получения дополнительной информации, демонстраций, учебников и примеров проектов.