mirror of
https://github.com/ershisan99/flashcards-docs.git
synced 2025-12-16 20:59:26 +00:00
lesson 3 temp
This commit is contained in:
@@ -8,5 +8,6 @@
|
|||||||
},
|
},
|
||||||
"lesson-1": "Урок 1",
|
"lesson-1": "Урок 1",
|
||||||
"lesson-2": "Урок 2: Формы",
|
"lesson-2": "Урок 2: Формы",
|
||||||
"lesson-3": "Урок 3: Таблицы"
|
"lesson-3": "Урок 3: Роутинг, Авторизация",
|
||||||
|
"extras": "Дополнительно"
|
||||||
}
|
}
|
||||||
|
|||||||
3
pages/extras/_meta.en.json
Normal file
3
pages/extras/_meta.en.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"tables": "Tables"
|
||||||
|
}
|
||||||
3
pages/extras/_meta.ru.json
Normal file
3
pages/extras/_meta.ru.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"tables": "Таблицы"
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 247 KiB |
1
pages/extras/tables.en.mdx
Normal file
1
pages/extras/tables.en.mdx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Under construction
|
||||||
254
pages/extras/tables.ru.mdx
Normal file
254
pages/extras/tables.ru.mdx
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
# Таблицы
|
||||||
|
|
||||||
|
## Сортировка
|
||||||
|
|
||||||
|
Наш бэкэнд будет принимать параметр sort формата `name-asc` где `name` -
|
||||||
|
название поля, а `asc` - направление сортировки. Возможные направления сортировки:
|
||||||
|
`asc` и `desc`.
|
||||||
|
|
||||||
|
Добавим возможность сортировки для таблиц, для этого:
|
||||||
|
|
||||||
|
- Создадим историю в сторибуке:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
title: 'Project A',
|
||||||
|
cardsCount: 10,
|
||||||
|
updated: '2023-07-07',
|
||||||
|
createdBy: 'John Doe',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Project B',
|
||||||
|
cardsCount: 5,
|
||||||
|
updated: '2023-07-06',
|
||||||
|
createdBy: 'Jane Smith',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Project C',
|
||||||
|
cardsCount: 8,
|
||||||
|
updated: '2023-07-05',
|
||||||
|
createdBy: 'Alice Johnson',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Project D',
|
||||||
|
cardsCount: 3,
|
||||||
|
updated: '2023-07-07',
|
||||||
|
createdBy: 'Bob Anderson',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Project E',
|
||||||
|
cardsCount: 12,
|
||||||
|
updated: '2023-07-04',
|
||||||
|
createdBy: 'Emma Davis',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
export const WithSort = {
|
||||||
|
render: () => {
|
||||||
|
return (
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Cards</th>
|
||||||
|
<th>Last Updated</th>
|
||||||
|
<th>Created by</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data.map(item => (
|
||||||
|
<tr key={item.title}>
|
||||||
|
<td>{item.title}</td>
|
||||||
|
<td>{item.cardsCount}</td>
|
||||||
|
<td>{item.updated}</td>
|
||||||
|
<td>{item.createdBy}</td>
|
||||||
|
<td>icons...</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Получим просто таблицу, которая пока не сортируемая.
|
||||||
|
|
||||||
|
- Создадим стейт для сортировки:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
type Sort = {
|
||||||
|
key: string
|
||||||
|
direction: 'asc' | 'desc'
|
||||||
|
} | null
|
||||||
|
|
||||||
|
const [sort, setSort] = useState<Sort>(null)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Добавим обработчик клика на заголовок таблицы:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const handleSort = (key: string) => {
|
||||||
|
if (sort && sort.key === key) {
|
||||||
|
setSort({
|
||||||
|
key,
|
||||||
|
direction: sort.direction === 'asc' ? 'desc' : 'asc',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setSort({
|
||||||
|
key,
|
||||||
|
direction: 'asc',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
и используем его в таблице:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<tr>
|
||||||
|
<th onClick={() => handleSort('name')}>Name</th>
|
||||||
|
<th onClick={() => handleSort('cardsCount')}>Cards</th>
|
||||||
|
<th onClick={() => handleSort('updated')}>Last Updated</th>
|
||||||
|
<th onClick={() => handleSort('createdBy')}>Created by</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
```
|
||||||
|
|
||||||
|
- Добавим иконки в ячейки заголовка:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<th onClick={() => handleSort('name')}>
|
||||||
|
Name
|
||||||
|
{sort && sort.key === 'name' && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
|
||||||
|
</th>
|
||||||
|
```
|
||||||
|
|
||||||
|
- Добавим `console.log()` для проверки стейта:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
console.log(sort)
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверяем, при клике должна меняться иконка и в консоли должен появляться правильный объект.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Рефакторинг
|
||||||
|
|
||||||
|
Мы повторяем слишком много кода в обработчиках, из-за чего мы оставляем слишком много шансов для ошибок.
|
||||||
|
|
||||||
|
Одним из возможных решений в данной ситуации будет создание массива с заголовками таблицы и использование его для отрисовки заголовков и обработчиков:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: 'Name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cardsCount',
|
||||||
|
title: 'Cards',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'updated',
|
||||||
|
title: 'Last Updated',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'createdBy',
|
||||||
|
title: 'Created by',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
{
|
||||||
|
columns.map(column => (
|
||||||
|
<th key={column.key} onClick={() => handleSort(column.key)}>
|
||||||
|
{column.title}
|
||||||
|
{sort && sort.key === column.key && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
|
||||||
|
</th>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Протипизируем columns:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
type Column = {
|
||||||
|
key: string
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
const columns: Array<Column> = ...
|
||||||
|
```
|
||||||
|
|
||||||
|
У нас будет несколько таблиц, поэтому имеет смысл вынести часть логики в отдельный компонент:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export const Header: FC<
|
||||||
|
Omit<
|
||||||
|
ComponentPropsWithoutRef<'thead'> & {
|
||||||
|
columns: Column[]
|
||||||
|
sort?: Sort
|
||||||
|
onSort?: (sort: Sort) => void
|
||||||
|
},
|
||||||
|
'children'
|
||||||
|
>
|
||||||
|
> = ({ columns, sort, onSort, ...restProps }) => {
|
||||||
|
const handleSort = (key: string, sortable?: boolean) => () => {
|
||||||
|
if (!onSort || !sortable) return
|
||||||
|
|
||||||
|
if (sort?.key !== key) return onSort({ key, direction: 'asc' })
|
||||||
|
|
||||||
|
if (sort.direction === 'desc') return onSort(null)
|
||||||
|
|
||||||
|
return onSort({
|
||||||
|
key,
|
||||||
|
direction: sort?.direction === 'asc' ? 'desc' : 'asc',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<thead {...restProps}>
|
||||||
|
<tr>
|
||||||
|
{columns.map(({ title, key, sortable }) => (
|
||||||
|
<th key={key} onClick={handleSort(key, sortable)}>
|
||||||
|
{title}
|
||||||
|
{sort && sort.key === key && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Обратите внимание, что состоянием мы будем управлять снаружи, а не внутри компонента.
|
||||||
|
|
||||||
|
Используем новый компонент:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<table>
|
||||||
|
<Header columns={columns} sort={sort} onSort={setSort} />
|
||||||
|
{/*...*/}
|
||||||
|
</table>
|
||||||
|
```
|
||||||
|
|
||||||
|
И, наконец, создадим нужную нам строку для бэкэнда:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const sortedString = useMemo(() => {
|
||||||
|
if (!sort) return null
|
||||||
|
|
||||||
|
return `${sort.key}-${sort.direction}`
|
||||||
|
}, [sort])
|
||||||
|
|
||||||
|
console.log(sortedString)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Самостоятельная работа:
|
||||||
|
|
||||||
|
- Добавить сортировку по умолчанию (при третьем клике по заголовку таблицы сортировка должна сбрасываться (null))
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"chapter-1": "Chapter 1"
|
"tables": "Tables"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"chapter-1": "Глава 1.Таблицы"
|
"chapter-1": "Роутинг"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
# Under construction
|
# Under Construction
|
||||||
|
|||||||
@@ -1,254 +1,85 @@
|
|||||||
# Таблицы
|
# Роутинг и защита страниц авторизацией
|
||||||
|
|
||||||
## Сортировка
|
## Роутинг
|
||||||
|
|
||||||
Наш бэкэнд будет принимать параметр sort формата `name-asc` где `name` -
|
Установим `react-router-dom`:
|
||||||
название поля, а `asc` - направление сортировки. Возможные направления сортировки:
|
|
||||||
`asc` и `desc`.
|
|
||||||
|
|
||||||
Добавим возможность сортировки для таблиц, для этого:
|
```bash filename="Terminal"
|
||||||
|
pnpm i react-router-dom
|
||||||
|
```
|
||||||
|
|
||||||
- Создадим историю в сторибуке:
|
Создадим файл `src/router.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx filename="src/router.tsx"
|
||||||
const data = [
|
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
title: 'Project A',
|
path: '/',
|
||||||
cardsCount: 10,
|
element: <div>hello</div>,
|
||||||
updated: '2023-07-07',
|
|
||||||
createdBy: 'John Doe',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Project B',
|
|
||||||
cardsCount: 5,
|
|
||||||
updated: '2023-07-06',
|
|
||||||
createdBy: 'Jane Smith',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Project C',
|
|
||||||
cardsCount: 8,
|
|
||||||
updated: '2023-07-05',
|
|
||||||
createdBy: 'Alice Johnson',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Project D',
|
|
||||||
cardsCount: 3,
|
|
||||||
updated: '2023-07-07',
|
|
||||||
createdBy: 'Bob Anderson',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Project E',
|
|
||||||
cardsCount: 12,
|
|
||||||
updated: '2023-07-04',
|
|
||||||
createdBy: 'Emma Davis',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
export const WithSort = {
|
|
||||||
render: () => {
|
|
||||||
return (
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Cards</th>
|
|
||||||
<th>Last Updated</th>
|
|
||||||
<th>Created by</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{data.map(item => (
|
|
||||||
<tr key={item.title}>
|
|
||||||
<td>{item.title}</td>
|
|
||||||
<td>{item.cardsCount}</td>
|
|
||||||
<td>{item.updated}</td>
|
|
||||||
<td>{item.createdBy}</td>
|
|
||||||
<td>icons...</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
export const Router = () => {
|
||||||
|
return <RouterProvider router={router} />
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Получим просто таблицу, которая пока не сортируемая.
|
Отрендерим его в `App`:
|
||||||
|
|
||||||
- Создадим стейт для сортировки:
|
```tsx filename="src/App.tsx"
|
||||||
|
import { Router } from '@/router'
|
||||||
|
|
||||||
```tsx
|
export function App() {
|
||||||
type Sort = {
|
return <Router />
|
||||||
key: string
|
|
||||||
direction: 'asc' | 'desc'
|
|
||||||
} | null
|
|
||||||
|
|
||||||
const [sort, setSort] = useState<Sort>(null)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Добавим обработчик клика на заголовок таблицы:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
const handleSort = (key: string) => {
|
|
||||||
if (sort && sort.key === key) {
|
|
||||||
setSort({
|
|
||||||
key,
|
|
||||||
direction: sort.direction === 'asc' ? 'desc' : 'asc',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
setSort({
|
|
||||||
key,
|
|
||||||
direction: 'asc',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
и используем его в таблице:
|
## Защита страниц авторизацией
|
||||||
|
|
||||||
```tsx
|
Разделим наши роуты на две переменные - `publicRoutes` и `privateRoutes`:
|
||||||
<tr>
|
|
||||||
<th onClick={() => handleSort('name')}>Name</th>
|
|
||||||
<th onClick={() => handleSort('cardsCount')}>Cards</th>
|
|
||||||
<th onClick={() => handleSort('updated')}>Last Updated</th>
|
|
||||||
<th onClick={() => handleSort('createdBy')}>Created by</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Добавим иконки в ячейки заголовка:
|
```tsx filename="src/router.tsx"
|
||||||
|
import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router-dom'
|
||||||
|
|
||||||
```tsx
|
const publicRoutes: RouteObject[] = [
|
||||||
<th onClick={() => handleSort('name')}>
|
|
||||||
Name
|
|
||||||
{sort && sort.key === 'name' && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
|
|
||||||
</th>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Добавим `console.log()` для проверки стейта:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
console.log(sort)
|
|
||||||
```
|
|
||||||
|
|
||||||
Проверяем, при клике должна меняться иконка и в консоли должен появляться правильный объект.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Рефакторинг
|
|
||||||
|
|
||||||
Мы повторяем слишком много кода в обработчиках, из-за чего мы оставляем слишком много шансов для ошибок.
|
|
||||||
|
|
||||||
Одним из возможных решений в данной ситуации будет создание массива с заголовками таблицы и использование его для отрисовки заголовков и обработчиков:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
const columns = [
|
|
||||||
{
|
{
|
||||||
key: 'name',
|
path: '/login',
|
||||||
title: 'Name',
|
element: <div>login</div>,
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'cardsCount',
|
|
||||||
title: 'Cards',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'updated',
|
|
||||||
title: 'Last Updated',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'createdBy',
|
|
||||||
title: 'Created by',
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
// ...
|
const privateRoutes: RouteObject[] = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
element: <div>hello</div>,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
{
|
const router = createBrowserRouter([...privateRoutes, ...publicRoutes])
|
||||||
columns.map(column => (
|
|
||||||
<th key={column.key} onClick={() => handleSort(column.key)}>
|
|
||||||
{column.title}
|
|
||||||
{sort && sort.key === column.key && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
|
|
||||||
</th>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Протипизируем columns:
|
Создадим компонент `PrivateRoutes`:
|
||||||
|
|
||||||
```tsx
|
```tsx filename="src/router.tsx"
|
||||||
type Column = {
|
function PrivateRoutes() {
|
||||||
key: string
|
const isAuthenticated = false
|
||||||
title: string
|
|
||||||
}
|
|
||||||
const columns: Array<Column> = ...
|
|
||||||
```
|
|
||||||
|
|
||||||
У нас будет несколько таблиц, поэтому имеет смысл вынести часть логики в отдельный компонент:
|
return isAuthenticated ? <Outlet /> : <Navigate to="/login" />
|
||||||
|
|
||||||
```tsx
|
|
||||||
export const Header: FC<
|
|
||||||
Omit<
|
|
||||||
ComponentPropsWithoutRef<thead> & {
|
|
||||||
columns: Column[]
|
|
||||||
sort?: Sort
|
|
||||||
onSort?: (sort: Sort) => void
|
|
||||||
},
|
|
||||||
'children'
|
|
||||||
>
|
|
||||||
> = ({ columns, sort, onSort, ...restProps }) => {
|
|
||||||
const handleSort = (key: string, sortable?: boolean) => () => {
|
|
||||||
if (!onSort || !sortable) return
|
|
||||||
|
|
||||||
if (sort?.key !== key) return onSort({ key, direction: 'asc' })
|
|
||||||
|
|
||||||
if (sort.direction === 'desc') return onSort(null)
|
|
||||||
|
|
||||||
return onSort({
|
|
||||||
key,
|
|
||||||
direction: sort?.direction === 'asc' ? 'desc' : 'asc',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<thead {...restProps}>
|
|
||||||
<tr>
|
|
||||||
{columns.map(({ title, key, sortable }) => (
|
|
||||||
<th key={key} onClick={handleSort(key, sortable)}>
|
|
||||||
{title}
|
|
||||||
{sort && sort.key === key && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Обратите внимание, что состоянием мы будем управлять снаружи, а не внутри компонента.
|
И обернем им наши privateRoutes:
|
||||||
|
|
||||||
Используем новый компонент:
|
```tsx filename="src/router.tsx"
|
||||||
|
const router = createBrowserRouter([
|
||||||
```tsx
|
{
|
||||||
<table>
|
element: <PrivateRoutes />,
|
||||||
<Header columns={columns} sort={sort} onSort={setSort} />
|
children: privateRoutes,
|
||||||
{/*...*/}
|
},
|
||||||
</table>
|
...publicRoutes,
|
||||||
|
])
|
||||||
```
|
```
|
||||||
|
|
||||||
И, наконец, создадим нужную нам строку для бэкэнда:
|
Теперь когда `isAuthenticated` будет `false`,
|
||||||
|
при переходе на `/` мы будем перенаправлены на `/login`.
|
||||||
```tsx
|
|
||||||
const sortedString = useMemo(() => {
|
|
||||||
if (!sort) return null
|
|
||||||
|
|
||||||
return `${sort.key}-${sort.direction}`
|
|
||||||
}, [sort])
|
|
||||||
|
|
||||||
console.log(sortedString)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Самостоятельная работа:
|
|
||||||
|
|
||||||
- Добавить сортировку по умолчанию (при третьем клике по заголовку таблицы сортировка должна сбрасываться (null))
|
|
||||||
|
|||||||
61
pages/lesson-3/chapter-2.ru.mdx
Normal file
61
pages/lesson-3/chapter-2.ru.mdx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# RTK Query
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
```bash filename="Terminal"
|
||||||
|
pnpm i @reduxjs/toolkit react-redux
|
||||||
|
```
|
||||||
|
|
||||||
|
## Создание стора
|
||||||
|
|
||||||
|
```ts filename="src/services/store.ts"
|
||||||
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
export const store = configureStore({
|
||||||
|
reducer: {},
|
||||||
|
middleware: getDefaultMiddleware => getDefaultMiddleware(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type AppDispatch = typeof store.dispatch
|
||||||
|
export type RootState = ReturnType<typeof store.getState>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Подключение стора к приложению
|
||||||
|
|
||||||
|
```tsx filename="src/App.tsx"
|
||||||
|
import { Provider } from 'react-redux'
|
||||||
|
|
||||||
|
import { Router } from '@/router'
|
||||||
|
import { store } from '@/services/store'
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
<Router />
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Создание Api
|
||||||
|
|
||||||
|
```ts filename="src/services/auth/auth.ts"
|
||||||
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||||
|
|
||||||
|
export const authApi = createApi({
|
||||||
|
reducerPath: 'authApi',
|
||||||
|
baseQuery: fetchBaseQuery({
|
||||||
|
baseUrl: 'https://api.flashcards.andrii.es',
|
||||||
|
credentials: 'include',
|
||||||
|
}),
|
||||||
|
endpoints: builder => {
|
||||||
|
return {
|
||||||
|
getMe: builder.query<any, void>({
|
||||||
|
query: () => `auth/me`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const { useGetMeQuery } = authApi
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user