Создаем таблицу с сортировкой строк на React JS
Если вы пишите frontend на модных фреймворках (react, angular, vue), и надо сделать сортировку таблиц, то для этого не нужно использовать какие либо плагины. Компонент для сортировки таблиц можно сделать самому. В этой статье я покажу, на примере React JS компонента, один из способов, как можно сделать свой плагин для вывода таблиц с сортировкой. Существуют несколько видов сортировок, по убыванию и по возрастанию, мы реализуем оба вида.
Шаг первый - создаем структуру данных
Начнем с нашей структуры данных, которыми мы будем оперировать. Мы будем работать с многомерными массивами. Они выглядят так
const arr = [
[123, 345, 567],
[645, 834, 965],
[837, 275, 345],
]
Как видите это массив массивов. Если у вас другая структура, приведите ее к такому виду.
Шаг второй - создаем компоненты
Создадим компонент под нашу таблицу. Начнем с
import React from 'react';
export default class SortableTable React.Component {
static propTypes = {
name: React.PropTypes.string,
};
constructor(props) {
super(props);
}
render() {
return (
<div></div>
);
}
}
Вызывать этот компонент будем так:
import SortableTable from 'path to sortableTable component';
const data = [
['Alexander', 345345, 887423],
['Paul', 2347, 76323],
['Larisa', 745, 54234],
]
<SortableTable data={data}>
Наша мини таблица будет примерно такого вида:
Name | ID | Count |
---|---|---|
Alexander | 345345 | 887423 |
Paul | 2347 | 76323 |
Larisa | 745 | 54234 |
Список того, что нужно сделать:
- в
SortableTable
компоненте нужно получить props и передать их во внутренний state компонента - создать 2 мини компонента, для отображения шапки таблицы и ее тела
- для шапки таблицы надо создать две стрелки для изменения вида сортировки
- создать функцию при клике на колонку в шапке таблицы, мы запускаем сортировку
Прокидываем props во внутренний state компонента
Для этого нам понадобится 2 внутренних метода React JS компонента, componentWillMount
и componentWillReceiveProps
.
ComponentWillMount сработает, когда React смонтирует компонент, а componentWillReceiveProps, когда у компонента изменятся props, например, когда мы запросим данные от сервера и передадим их сразу в компонент.
Код будет следующим:
import React from 'react';
export default class SortableTable extends React.Component {
static propTypes = {
data: React.PropTypes.array,
};
constructor(props) {
super(props);
this.state = {
data: [],
columns: [],
}
}
componentWillMount() {
const { data } = this.props;
this.setState({ data })
}
componentWillReceiveProps(nextProps) {
const { data } = nextProps;
this.setState({ data })
}
render() {
return (
<div></div>
);
}
}
Создаем два мини компонента
Разбивка большого компонента на группу маленьких - очень хорошая практика, потому что их можно переиспользовать в других файлах и проектах.
Для начала вынесем в начало файла, после обьявления React, нашу переменную с текстом шапки таблицы
const TABLE_COLUMNS = [
{
label: 'Name',
sort: 'default',
},{
label: 'ID',
sort: 'default',
},{
label: 'Count',
sort: 'default',
}
];
В поле label
находится сам текст, а в поле sort
тип сортировки, по умолчанию он равен default
.
Создадим в том же файле два компонента:
const SortableHeader = (props) => {
return(
<thead>
<tr>
{TABLE_COLUMNS.map((element, index) =>
<th key={index}>{element.label}</th>
)}
</tr>
</thead>
)
}
Для маленьких компонентов удобно использовать Stateless Functional Components, главное отличие от обычных компонентов, это отсутствие внутреннего state.
В этом коде мы
- возвращаем элементы
- перебираем наш массив с текстом заголовков шапки
- присваиваем встроенному атрибуту key индекс элемента
- выводим текст в элементе
th
Добавляем компонент для тела нашей таблицы:
const SortableBody = ({data}) => {
return(
<tbody>
{data.map((element, index) =>
<tr key={index}>
{element.map((item, i) =>
<td key={i}>{item}</td>
)}
</tr>
)}
</tbody>
)
}
В этом коде мы
- перебираем массив в переданных props от родительского компонента
SortableTable
- перебираем вложенные массивы с данными
Немного меняем наш локальный state
Для доступа и изменения даных, надо добавить TABLE_COLUMNS в this.state
constructor(props) {
super(props);
this.state = {
data: [],
column: TABLE_COLUMNS,
}
}
Шаг третий - добавляем стили для отображения стрелок
Создаем стрелки для показа пользователю, в каком направлении идет сортировка.
Для это нам понадобится подключить модуль bem-cn
. Я его использую для генерирования классов для компонентов, вы можете использовать любую другую библиотеку или использовать логические выражения, но с bem-cn код выглядит локаничнее.
После обьявления нашей константы, всталяем код:
const cn = require('bem-cn')('table');
Добавляем в проект файл со стилями, такого содержания:
.sorting-block{
position: relative;
padding-right: 20px !important;
cursor: pointer;
user-select: none;
&.is-sortASC{
&:before{
border-bottom-color: #333;
}
}
&.is-sortDESC{
&:after{
border-top-color: #333;
}
}
&:before, &:after{
position: absolute;
content: '';
right: 5px;
}
&:before{
top: 50%;
margin-top: -5px;
width: 0;
height: 0;
border-style: solid;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #eee transparent;
}
&:after{
top: 50%;
margin-top: 1px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #eee transparent transparent transparent;
}
}
Этот код добавит две стрелочки к названию и они не будут активны, но как только кликнем, в зависимости от направления сортировки, применится соответствующий класс, и мы увидим активную стрелочку. Если вид сортировки asc, то добавляем класс sortASC, и на оборот, если сортируем по desc, добавляем класс sortDESC. По умолчанию у нас не добавляется никакого класса. С динамическим добавлением или удалением класса хорошо справляется модуль bem-cn
. Он за нас сделает много работы.
Шаг четвертый - добавляем обработчик
Перепишем наш render метод в SortableTable компоненте
render() {
return (
<table className={cn}>
<SortableHeader columns={this.state.columns} />
<SortableBody data={this.state.data} />
</table>
);
}
Передадим на вход данные о наших столбцах в column props. А также добавим обработчик по клику на элемент через props.
<SortableHeader columns={this.state.columns} onClick={this.sortTableFunc} />
Следующим этапом нам надо подключить модуль sort-multidimensional-array-func
. Он экспортирует функцию, которая принимает на вход массив, индекс элемента, метод сортировки, asc или desc в виде строки и возвращает отсортированный массив, который мы преобразуем в таблицу.
$ npm i -S sort-multidimensional-array-func
Импортируем функцию sortMultidimensionalArrayFunc в наш проект:
import sortMultidimensionalArrayFunc from 'sort-multidimensional-array-func';
Напишем нашу функцию-обработчик
sortTableFunc = (id, sortMethod) => {
const { data, columns } = this.state;
let currentSortMethod = 'default';
switch (sortMethod) {
case 'default':
currentSortMethod = 'asc';
break;
case 'asc':
currentSortMethod = 'desc';
break;
case 'desc':
currentSortMethod = 'asc';
break;
default:
currentSortMethod = 'asc';
}
const changeColumn = columns.map((e, i) =>
({ ...e, sort: i == id ? currentSortMethod : 'default' })
);
const sortData = sortMultidimensionalArrayFunc(data, id, currentSortMethod);
this.setState({
data: sortData,
columns: changeColumn,
});
}
А так же перепишем наш SortableHeader
компонент
const SortableHeader = (props) => {
const { columns, onClick } = props;
return(
<thead>
<tr>
{columns.map((element, index) =>
<th
key={index}
className={cn('sorting').state({
sortASC: element.sort == 'asc',
sortDESC: element.sort == 'desc',
}).mix('sorting-block text-nowrap')}
onClick={() => onClick(index, element.sort)}
>
{element.label}
</th>
)}
</tr>
</thead>
);
}
После этого у вас должен получиться такой код:
import React from 'react';
import sortMultidimensionalArrayFunc from 'sort-multidimensional-array-func';
const cn = require('bem-cn')('table');
const TABLE_COLUMNS = [
{
label: 'Name',
sort: 'default',
},
{
label: 'ID',
sort: 'default',
},
{
label: 'Count',
sort: 'default',
},
];
const SortableHeader = (props) => {
const { columns, onClick } = props;
return(
<thead>
<tr>
{columns.map((element, index) =>
<th
key={index}
className={cn('sorting').state({
sortASC: element.sort == 'asc',
sortDESC: element.sort == 'desc',
}).mix('sorting-block text-nowrap')}
onClick={() => onClick(index, element.sort)}
>
{element.label}
</th>
)}
</tr>
</thead>
);
}
const SortableBody = (props) => {
const { data } = props;
return(
<tbody>
{data.map((element, index) =>
<tr key={index}>
{element.map((item, i) =>
<td key={i}>{item}</td>
)}
</tr>
)}
</tbody>
);
}
export default class SortableTable extends React.Component {
static propTypes = {
data: React.PropTypes.array,
};
constructor(props) {
super(props);
this.state = {
data: [],
columns: TABLE_COLUMNS,
};
}
componentWillMount() {
const { data } = this.props;
this.setState({ data });
}
componentWillReceiveProps(nextProps) {
const { data } = nextProps;
this.setState({ data });
}
render() {
return (
<table className={cn}>
<SortableHeader columns={this.state.columns} onClick={this.sortTableFunc} />
<SortableBody data={this.state.data} />
</table>
);
}
sortTableFunc = (id, sortMethod) => {
const { data, columns } = this.state;
let currentSortMethod = 'default';
switch (sortMethod) {
case 'default':
currentSortMethod = 'asc';
break;
case 'asc':
currentSortMethod = 'desc';
break;
case 'desc':
currentSortMethod = 'asc';
break;
default:
currentSortMethod = 'asc';
}
const changeColumn = columns.map((e, i) =>
({ ...e, sort: i == id ? currentSortMethod : 'default' })
);
const sortData = sortMultidimensionalArrayFunc(data, id, currentSortMethod);
this.setState({
data: sortData,
columns: changeColumn,
});
}
}
Этот компонент довольно много универсальный, его можно переиспользовать и создавать разные таблицы.
- Колличество слов
- 2750
- Дата создания
- 08 марта 2017