# Таблицы ## Сортировка Наш бэкэнд будет принимать параметр 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 ( {data.map(item => ( ))}
Name Cards Last Updated Created by
{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) ``` Проверяем, при клике должна меняться иконка и в консоли должен появляться правильный объект. ![sort](./images/table-with-basic-sort.png) ## Рефакторинг Мы повторяем слишком много кода в обработчиках, из-за чего мы оставляем слишком много шансов для ошибок. Одним из возможных решений в данной ситуации будет создание массива с заголовками таблицы и использование его для отрисовки заголовков и обработчиков: ```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))