refine — это внутренний инструментальный фреймворк React без головы. Он помогает быстро развиваться при разработке B2B и B2C приложений. Ускоряя вашу работу, он никогда не ограничивает вас и имеет полностью настраиваемую структуру.
Ключевые особенности.
🔥 Безголовый: вы можете принести свой собственный UI и заправить его с помощью Refine для максимальной скорости разработки.
⚙️ Нулевая конфигурация: Однолинейная настройка с помощью суперплатформы. Запуск проекта занимает меньше минуты.
📦 Из коробки: Маршрутизация, сеть, аутентификация, управление состоянием, i18n и UI.
🔌 Независимость от бэкенда: подключается к любому пользовательскому бэкенду. Встроенная поддержка REST API, GraphQL, NestJs CRUD, Airtable, Strapi, Strapi v4, Strapi GraphQL, Supabase, Hasura, Appwrite, Firebase и Altogic.
📝 Native Typescript Core : Вы всегда можете отказаться от использования обычного JavaScript.
🐜 Enterprise UI : Работает без проблем с Ant Design System. (Поддержка нескольких фреймворков UI находится в дорожной карте).
📝 Код без кодовых пластин: Сохраняет вашу кодовую базу чистой и читабельной.
Вы можете использовать его с любой библиотекой пользовательского интерфейса без проблем. Также поддерживается Ant Design как «из коробки».
refine напрямую предоставляет компоненты Ant Design и некоторые хуки для работы с этими компонентами. Эти хуки предоставляют вам необходимые реквизиты для этих компонентов Ant Design.
Расширенный учебник по Refine
В этой статье мы рассмотрим базовый учебник Refine. Поэтому я предлагаю вам прочитать базовый учебник по Refine.
В этом руководстве мы узнаем, как включить функции (i18n, Realtime, Access Control), предоставляемые refine, в наш проект и как мы можем их использовать.
Создание проекта Refine
Давайте начнем с создания нашего проекта refine. Вы можете использовать супершаблон для создания проекта refine.
npx superplate-cli -p refine-react refine-advanced-tutorial
✔ What will be the name of your app › refine-advanced-tutorial
✔ Package manager: · npm
✔ Do you want to using UI Framework?: · antd
✔ Do you want to customize theme?: · css
✔ Data Provider: · custom-json-rest-data-provider
✔ Auth Provider: · none
✔ Do you want to add an example page? · example-resource
✔ Do you want to customize layout? · no
✔ Вы хотите добавить страницу с примером? -example-resource
Выбрав этот пункт, вы сможете просматривать учебник в локальном режиме.
cd refine-advanced-tutorial
npm run dev
Как вы видите, наш пример проекта готов. Теперь давайте рассмотрим, как функции, предлагаемые refine, включаются в проект и как они используются.
Добавление i18n Provider в ваш проект
Создание экземпляра i18n
Сначала мы создадим экземпляр i18n с помощью react-i18next.
src/i18n.ts:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-xhr-backend";
import detector from "i18next-browser-languagedetector";
i18n
.use(Backend)
.use(detector)
.use(initReactI18next)
.init({
supportedLngs: ["en", "de"],
backend: {
loadPath: "/locales/{{lng}}/{{ns}}.json",
},
defaultNS: "common",
fallbackLng: ["en", "de"],
});
export default i18n;
Импортируем экземпляр i18n
, который мы создали в файле index.tsx. Затем обернем приложение в React.Suspense
.
src/index.tsx:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./i18n";
ReactDOM.render(
<React.StrictMode>
<React.Suspense fallback="loading">
<App />
</React.Suspense>
</React.StrictMode>,
document.getElementById("root"),
);
Давайте определим наш i18n-провайдер и доработаем его.
src/App.tsx:
import { Refine } from "@pankod/refine-core";
import { notificationProvider, Layout } from "@pankod/refine-antd";
import routerProvider from "@pankod/refine-react-router";
import "@pankod/refine-antd/dist/styles.min.css";
import dataProvider from "@pankod/refine-simple-rest";
import { PostList, PostCreate, PostEdit, PostShow } from "pages/posts";
import { useTranslation } from "react-i18next";
function App() {
const { t, i18n } = useTranslation();
const i18nProvider = {
translate: (key: string, params: object) => t(key, params),
changeLocale: (lang: string) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
};
return (
<Refine
routerProvider={routerProvider}
notificationProvider={notificationProvider}
Layout={Layout}
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
show: PostShow,
},
]}
i18nProvider={i18nProvider}
/>
);
}
export default App;
Теперь давайте добавим наш собственный перевод. Создадим два отдельных json-файла на английском и немецком языках.
/public/locales/en/common.json
{
"posts": {
"posts": "Posts",
"fields": {
"id": "Id",
"title": "Title",
"category": "Category",
"status": {
"title": "Status",
"published": "Published",
"draft": "Draft",
"rejected": "Rejected"
},
"content": "Content",
"createdAt": "Created At"
},
"titles": {
"create": "Create Post",
"edit": "Edit Post",
"list": "Posts",
"show": "Show Post"
}
},
"table": {
"actions": "Actions"
}
}
/public/locales/de/common.json
{
"posts": {
"posts": "Einträge",
"fields": {
"id": "Id",
"title": "Titel",
"category": "Kategorie",
"status": {
"title": "Status",
"published": "Veröffentlicht",
"draft": "Draft",
"rejected": "Abgelehnt"
},
"content": "Inhalh",
"createdAt": "Erstellt am"
},
"titles": {
"create": "Erstellen",
"edit": "Bearbeiten",
"list": "Einträge",
"show": "Eintrag zeigen"
}
},
"table": {
"actions": "Aktionen"
}
}
В этой статье в качестве примера мы включили перевод только небольшой части.
Теперь давайте создадим компонент select в заголовке и рассмотрим наши посты в соответствии с выбранным языком.
src/components/header.tsx:
import { useGetLocale, useSetLocale } from "@pankod/refine-core";
import {
AntdLayout,
Space,
Menu,
Button,
Icons,
Dropdown,
} from "@pankod/refine-antd";
import { useTranslation } from "react-i18next";
const { DownOutlined } = Icons;
export const Header: React.FC = () => {
const { i18n } = useTranslation();
const locale = useGetLocale();
const changeLanguage = useSetLocale();
const currentLocale = locale();
const menu = (
<Menu selectedKeys={[currentLocale]}>
{[...(i18n.languages || [])].sort().map((lang: string) => (
<Menu.Item key={lang} onClick={() => changeLanguage(lang)}>
{lang === "en" ? "English" : "German"}
</Menu.Item>
))}
</Menu>
);
return (
<AntdLayout.Header
style={{
display: "flex",
justifyContent: "flex-end",
alignItems: "center",
padding: "0px 24px",
height: "48px",
backgroundColor: "#FFF",
}}
>
<Dropdown overlay={menu}>
<Button type="link">
<Space>
{currentLocale === "en" ? "English" : "German"}
<DownOutlined />
</Space>
</Button>
</Dropdown>
</AntdLayout.Header>
);
};
Давайте определим заголовок, который мы создали в рамках уточнения.
return (
<Refine
routerProvider={routerProvider}
notificationProvider={notificationProvider}
Layout={Layout}
i18nProvider={i18nProvider}
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
Header={Header}
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
show: PostShow,
},
]}
/>
);
Теперь наш i18n Provider готов к использованию, давайте протестируем его вместе.
Использование перевода с содержимым таблицы
import {
useTranslate,
useMany,
} from "@pankod/refine-core";
import {
List,
Table,
TextField,
useTable,
Space,
EditButton,
ShowButton,
} from "@pankod/refine-antd";
import { IPost, ICategory } from "interfaces";
export const PostList: React.FC = () => {
const translate = useTranslate();
const { tableProps } = useTable<IPost>();
const categoryIds =
tableProps?.dataSource?.map((item) => item.category.id) ?? [];
const { data, isLoading } = useMany<ICategory>({
resource: "categories",
ids: categoryIds,
queryOptions: {
enabled: categoryIds.length > 0,
},
});
return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="ID" />
<Table.Column
dataIndex="title"
title={translate("posts.fields.title")}
/>
<Table.Column
dataIndex={["category", "id"]}
title={translate("posts.fields.category")}
render={(value) => {
if (isLoading) {
return <TextField value="Loading..." />;
}
return (
<TextField
value={
data?.data.find((item) => item.id === value)
?.title
}
/>
);
}}
/>
<Table.Column<IPost>
title={translate("table.actions")}
dataIndex="actions"
key="actions"
render={(_value, record) => (
<Space>
<EditButton size="small" recordItemId={record.id} />
<ShowButton size="small" recordItemId={record.id} />
</Space>
)}
/>
</Table>
</List>
);
};
С помощью refine i18n Provider вы можете добавить нужный вам перевод и организовать содержимое на разных языках.
Ознакомьтесь с refine i18n Provider для получения более подробной информации и пошагового руководства.
Добавьте Live Provider (Realtime) в ваш проект с помощью Refine
refine позволяет добавить поддержку реального времени в ваше приложение с помощью реквизита liveProvider для . Он может быть использован для обновления и отображения данных в режиме реального времени в вашем приложении.
Теперь давайте сделаем наше приложение работающим в реальном времени с помощью refine Live Provider.
В этой статье мы будем использовать Ably для обеспечения функций реального времени.
Установка
Нам нужно установить пакет Ably live provider из refine.
npm install @pankod/refine-ably
Сначала создадим ably-client и определим наш API-ключ Ably.
src/utility/client.ts:
import { Ably } from "@pankod/refine-ably";
export const ablyClient = new Ably.Realtime("YOUR_ABLY_API_KEY");
Затем передадим liveProvider из @pankod/refine-ably в .
src/App.tsx:
import { Refine } from "@pankod/refine-core";
import { notificationProvider, Layout } from "@pankod/refine-antd";
import routerProvider from "@pankod/refine-react-router";
import "@pankod/refine-antd/dist/styles.min.css";
import dataProvider from "@pankod/refine-simple-rest";
import { liveProvider } from "@pankod/refine-ably";
import { ablyClient } from "utility";
import { PostList, PostCreate, PostEdit, PostShow } from "pages/posts";
import { Header } from "./components/header";
import { useTranslation } from "react-i18next";
function App() {
const { t, i18n } = useTranslation();
const i18nProvider = {
translate: (key: string, params: object) => t(key, params),
changeLocale: (lang: string) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
};
return (
<Refine
routerProvider={routerProvider}
notificationProvider={notificationProvider}
Layout={Layout}
i18nProvider={i18nProvider}
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
Header={Header}
liveProvider={liveProvider(ablyClient)}
liveMode="auto"
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
show: PostShow,
},
]}
/>
);
}
export default App;
Вы можете настроить liveMode
, в данном примере используется режим «auto».
Ознакомьтесь с Refine Live Provider для получения более подробной информации и пошагового руководства.
Наш проект теперь работает в реальном времени! Благодаря Refine Live Provider мы сделали наш проект Realtime, добавив всего 2 строки.
Давайте посмотрим, как работает наш проект RealTime.
Добавление контроля доступа к проекту с помощью Refine
Вы можете контролировать свой проект как угодно с помощью refine react admin framework. Теперь давайте добавим провайдер контроля доступа в наш проект refine.
Контроль доступа — это обширная тема, в которой существует множество продвинутых решений, предоставляющих различные возможности. refine намеренно агностичен для своего API, чтобы иметь возможность интегрировать различные методы (RBAC, ABAC, ACL и т.д.) и различные библиотеки (Casbin, CASL, Cerbos, AccessControl.js). Метод can будет точкой входа для этих решений.
За подробной информацией обращайтесь к документации Access Control Provider. →
Давайте создадим две роли, Admin и Editor. Admin имеет полные CRUD полномочия на посты. Роль Editor, с другой стороны, имеет полномочия только на создание и редактирование новых постов. Другими словами, человек в роли редактора не может удалять посты и не может просматривать все строки таблицы.
Давайте начнем с создания двух кнопок для ролей Admin
и Editor
в созданном нами компоненте заголовка.
/src/componets/header.tsx:
import { useGetLocale, useSetLocale } from "@pankod/refine-core";
import {
AntdLayout,
Space,
Menu,
Button,
Icons,
Dropdown,
Radio,
} from "@pankod/refine-antd";
import { useTranslation } from "react-i18next";
const { DownOutlined } = Icons;
interface HeaderProps {
role: string;
}
export const Header: React.FC<HeaderProps> = ({ role }) => {
const { i18n } = useTranslation();
const locale = useGetLocale();
const changeLanguage = useSetLocale();
const currentLocale = locale();
const menu = (
<Menu selectedKeys={[currentLocale]}>
{[...(i18n.languages || [])].sort().map((lang: string) => (
<Menu.Item key={lang} onClick={() => changeLanguage(lang)}>
{lang === "en" ? "English" : "German"}
</Menu.Item>
))}
</Menu>
);
return (
<AntdLayout.Header
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "0px 24px",
height: "48px",
backgroundColor: "#FFF",
}}
>
<Radio.Group
value={role}
onChange={(event) => {
localStorage.setItem("role", event.target.value);
location.reload();
}}
>
<Radio.Button value="admin">Admin</Radio.Button>
<Radio.Button value="editor">Editor</Radio.Button>
</Radio.Group>
<Dropdown overlay={menu}>
<Button type="link">
<Space>
{currentLocale === "en" ? "English" : "German"}
<DownOutlined />
</Space>
</Button>
</Dropdown>
</AntdLayout.Header>
);
};
В этой статье мы будем использовать Cerbos для усовершенствования контроля доступа.
npm install cerbos
После завершения установки создадим объект Cerbos в файле App.tsx и определим его в .
import { Cerbos } from "cerbos";
const cerbos = new Cerbos({
hostname: "https://demo-pdp.cerbos.cloud", // The Cerbos PDP instance
playgroundInstance: "WS961950bd85QNYlAvTmJYubP0bqF7e3", // The playground instance ID to test
});
<Refine
routerProvider={routerProvider}
notificationProvider={notificationProvider}
Layout={Layout}
i18nProvider={i18nProvider}
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
Header={() => <Header role={role} />}
liveProvider={liveProvider(ablyClient)}
liveMode="auto"
accessControlProvider={{
can: async ({ action, params, resource }) => {
const cerbosPayload = {
principal: {
id: "demoUser", // Fake a user ID
roles: [role],
// this is where user attributes can be passed
attr: {},
},
// the resouces being access - can be multiple
resource: {
kind: resource,
instances: {
[params?.id || "new"]: {
attr: params,
},
},
},
// the list of actions on the resource to check authorization for
actions: [action],
};
const result = await cerbos.check(cerbosPayload);
return Promise.resolve({
can: result.isAuthorized(params?.id || "new", action),
});
},
}}
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
show: PostShow,
canDelete: true,
},
]}
/>
Мы будем выполнять наши действия в соответствии с ролью, которую мы выберем в заголовке. Как вы можете видеть выше, мы установили это с помощью метода access Control Provider
can
.
Теперь с помощью уточняющего useCan
hook Выполним операции в соответствии с ролями в нашем списке.
src/pages/PostList.tsx:
import {
IResourceComponentsProps,
useMany,
useTranslate,
useCan,
} from "@pankod/refine-core";
import {
List,
Table,
TextField,
useTable,
Space,
EditButton,
ShowButton,
FilterDropdown,
useSelect,
Select,
Radio,
TagField,
NumberField,
} from "@pankod/refine-antd";
import { IPost, ICategory } from "interfaces";
export const PostList: React.FC<IResourceComponentsProps> = () => {
const translate = useTranslate();
const { tableProps } = useTable<IPost>();
const categoryIds =
tableProps?.dataSource?.map((item) => item.category.id) ?? [];
const { data, isLoading } = useMany<ICategory>({
resource: "categories",
ids: categoryIds,
queryOptions: {
enabled: categoryIds.length > 0,
},
});
const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
optionLabel: "title",
optionValue: "id",
});
const { data: canAccess } = useCan({
resource: "posts",
action: "field",
params: { field: "hit" },
});
return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="ID" />
<Table.Column
dataIndex="title"
title={translate("posts.fields.title")}
/>
<Table.Column
dataIndex={["category", "id"]}
title={translate("posts.fields.category")}
render={(value) => {
if (isLoading) {
return <TextField value="Loading..." />;
}
return (
<TextField
value={data?.data.find((item) => item.id === value)?.title}
/>
);
}}
filterDropdown={(props) => (
<FilterDropdown {...props}>
<Select
style={{ minWidth: 200 }}
mode="multiple"
placeholder="Select Category"
{...categorySelectProps}
/>
</FilterDropdown>
)}
/>
{canAccess?.can && (
<Table.Column
dataIndex="hit"
title="Hit"
render={(value: number) => (
<NumberField
value={value}
options={{
notation: "compact",
}}
/>
)}
/>
)}
<Table.Column
dataIndex="status"
title="Status"
render={(value: string) => <TagField value={value} />}
filterDropdown={(props: any) => (
<FilterDropdown {...props}>
<Radio.Group>
<Radio value="published">Published</Radio>
<Radio value="draft">Draft</Radio>
<Radio value="rejected">Rejected</Radio>
</Radio.Group>
</FilterDropdown>
)}
/>
<Table.Column<IPost>
title={translate("table.actions")}
dataIndex="actions"
render={(_, record) => (
<Space>
<EditButton hideText size="small" recordItemId={record.id} />
<ShowButton hideText size="small" recordItemId={record.id} />
</Space>
)}
/>
</Table>
</List>
);
};
Здесь, если выбранная роль — Admin, в нашей таблице появится раздел ‘Hit’. Мы указали, что роль Редактора не может отображать этот раздел.
Просмотрите статью Уточнение поставщика контроля доступа для получения более подробной информации и пошагового руководства
Заключение
В этом руководстве мы показали, насколько полезны и просты функции внутреннего инструмента Refine Framework. Эти функции значительно сократят ваше время разработки. Хотя Refine предлагает вам возможность быстрой разработки, он не ограничивает вас и дает возможность настраивать ваш проект по своему усмотрению.
Мы видели, как просто в Refine включить и использовать функции интернационализации (i18n), Live Provider (Realtime) и контроля доступа. С помощью refine вы можете разрабатывать более сложные приложения простым способом.
С refine react admin вы можете разработать любое веб-приложение, которое захотите, с Admin Panel
, базовым Crud App
или поддержкой Next.js-SSR.
refine предоставляет возможность разрабатывать B2B
и B2C
приложения без каких-либо ограничений и в полностью настраиваемом виде.
Смотрите подробную информацию о refine. →
Для получения информации о других возможностях refine →
Пример Live CodeSandbox