# Таблицы
## Сортировка
Наш бэкэнд будет принимать параметр 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 (
| Name |
Cards |
Last Updated |
Created by |
|
{data.map(item => (
| {item.title} |
{item.cardsCount} |
{item.updated} |
{item.createdBy} |
icons... |
))}
)
},
}
```
Получим просто таблицу, которая пока не сортируемая.
- Создадим стейт для сортировки:
```tsx
type Sort = {
key: string
direction: 'asc' | 'desc'
} | null
const [sort, setSort] = useState(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
| handleSort('name')}>Name |
handleSort('cardsCount')}>Cards |
handleSort('updated')}>Last Updated |
handleSort('createdBy')}>Created by |
|
```
- Добавим иконки в ячейки заголовка:
```tsx
handleSort('name')}>
Name
{sort && sort.key === 'name' && {sort.direction === 'asc' ? '▲' : '▼'}}
|
```
- Добавим `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 => (
handleSort(column.key)}>
{column.title}
{sort && sort.key === column.key && {sort.direction === 'asc' ? '▲' : '▼'}}
|
))
}
// ...
```
Протипизируем columns:
```tsx
type Column = {
key: string
title: string
}
const columns: Array = ...
```
У нас будет несколько таблиц, поэтому имеет смысл вынести часть логики в отдельный компонент:
```tsx
export const Header: FC<
Omit<
ComponentPropsWithoutRef & {
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 (
{columns.map(({ title, key, sortable }) => (
|
{title}
{sort && sort.key === key && {sort.direction === 'asc' ? '▲' : '▼'}}
|
))}
)
}
```
Обратите внимание, что состоянием мы будем управлять снаружи, а не внутри компонента.
Используем новый компонент:
```tsx
```
И, наконец, создадим нужную нам строку для бэкэнда:
```tsx
const sortedString = useMemo(() => {
if (!sort) return null
return `${sort.key}-${sort.direction}`
}, [sort])
console.log(sortedString)
```
## Самостоятельная работа:
- Добавить сортировку по умолчанию (при третьем клике по заголовку таблицы сортировка должна сбрасываться (null))