Редактировать
Меню
    наверх

    Создаем таблицу с сортировкой строк на 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}>
    

    Наша мини таблица будет примерно такого вида:

    NameIDCount
    Alexander345345887423
    Paul234776323
    Larisa74554234

    Список того, что нужно сделать:

    • в 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
    Комментарии