когда нечего делать, а творческая душа требует реализации и хочется что-то сделать или написать, то я пишу сюда или делаю что-то на этом сайте. Кому интересна web-разработка, wordpress и то что рядом с этим, то заходите на мой сайт и читайте всякое...
Метод фильтрации и сортировки массивов для передачи в компонент. React/Redux
Йо-йо! Всё моё время сейчас уходит на java script и react. Я также работаю в компании, которая продаёт автомобили онлайн. Недавно мы выкатили в «prod» новый дизайн списка сравнения. Прошлый генерировался с помощью php, а далее редактировался с помощью jQuery. Новый же написан на реакте. Плюс добавилась сортировка автомобилей.
Само собой я использую хранилище для данных, в том числе и списка автомобилей. Проблема в том, что если бы я сортировал и фильтровал данные в store то либо постепенно при фильтрации данные из него удалялись, либо пришлось иметь два экземпляра списка ( полный и фильтрованный/сортированный ).
Сейчас я хочу показать как я изящно избавился от этой проблемы, а ещё написал красивый метод для фильтрации и сортировки массивов.
Дано
import React, { Component } from 'react';
import TableColumns from './TableColumns'
import {connect} from "react-redux";
import _CTFI from '../../store/utils';
class TableColumnsContainer extends Component{
render(){
const {
tabSign, // какой таб? это параметр для фильтрации. В моём случае это все авто/новые/подержанные (all/new/used)
list, // Список
sortParam // Параметр для сортировки
} = this.props;
const filtredList = /* Нужно получить фильтрованный и сортированный список */
return(
<TableColumns
isMobile={isMobile}
toggleOpenSection={toggleOpenSection}
sections={sections}
list={filtredList}
tabSign={tabSign}
removeColumn={removeColumn}
/>
)
}
}
const mapStateToProps = (state) => ({
tabSign: state.tabSign,
list: state.list,
sections: state.creatableRows,
isMobile: state.isMobile,
sortParam: state.sortParam
});
export default connect(mapStateToProps, null)(TableColumnsContainer);
Ещё раз… У нас есть родительский компонент (hight order component) список (array, содержит объекты), таб по которому фильтруем(все/новые/c пробегом), параметр по которому фильтруем. Ну и собственно дочерний компонент в котором это все рисуется.
Алгоритм
Я уже говорил, что не буду изменять store, список я буду изменять в нашем родительском компоненте. Как именно:
Создам функцию, которая будет возвращать инстанс объекта (который тоже создам).
Создам новый объект с методами
Реализую методы фильтрации и сортировки
Реализую метод для возврата массива с новым списком
Вызову функцию на массиве из store и используя методы сделаю преобразования.
Шаг первый: создать функцию
Функция которая вернёт мне экземпляр объекта:
export default function _CTFI(array) {
if(Array.isArray(array)){
return new _CTF(array);
} else {
console.error('variable is not array') // Тут можете поменять как вам удобно
}
}
Бам! Так просто.
Шаг второй: Новый объект (класс)
Создадим новый объект:
const _CTF = function (vars) {
if(this instanceof _CTF){
this.data = JSON.parse(JSON.stringify(vars)); // Обязательно JJSON.parse(JSON.stringify(vars)) или другой способ deepСlone объектов, например _.cloneDeep
return this;
} else {
return new _CTF(vars);
}
}
Здесь я использую стандартные функции JS. В метод будет сообщаться состояние табов (all/new/used) и в зависимости от этого будет происходить фильтрация через метод filter.
Сортировка по параметрам:
_CTF.prototype.orderByParam = function (param) {
if(param === null){
return this;
} else {
switch(param){
case 'CarPrice':
this.data = this.data.sort(this._sortByPrice);
return this;
case 'IsMarkedDown':
this.data = this.data.sort(this._sortByMarkedDown);
return this;
case 'CarRun':
this.data = this.data.sort(this._sortByCarRun);
return this;
default:
console.warn(Нет правила для сортировки по параметру ${param});
return this;
}
}
}
Так как мы ранее сохраняли массив в нашем объекте в свойстве data то у нас нет проблем.
_CTF.prototype.get = function () {
return this.data;
}
Шаг пятый: Вернёмся в компонент
Мы должны получить наш новый список:
const { tabSign, // какой таб? это параметр для фильтрации. В моём случае это все авто/новые/подержанные (all/new/used)
list, // Список
sortParam // Параметр для сортировки
} = this.props;
const filtredList = _CTFI(list).filterByState(tabSign).orderByParam(sortParam).get();
Получился очень изящно и читабельно.
Можно ли использовать в redux?!
А почему нет?! Только нам нужно будет хранить экземпляр нашего списка без изменений. Мы можем использовать наши методы и для в action’на. Это будет удобно если мы используем наш список в нескольких местах. Например:
Мы же можем так делать огромные цепочки фильтрации:
_CTF.prototype.filterByParams= function (param, fnForFilter) {
if(param === null){
return this;
} else {
switch(param){
case 'CarPrice':
this.data = fnForFilter(this.data, param); // Различные функции для фильтрации
return this;
case 'IsMarkedDown':
this.data = fnForFilter(this.data, param);
return this;
case 'CarRun':
this.data = fnForFilter(this.data, param);
return this;
default:
return this;
}
}
}
Тогда мы сможем вызывать его так:
const filtredList = _CTFI(list)
.filterByParams('CarPrice', (data, param)=>{
return data.filter((obj)=>{ obj[param] > 100000 })
})
.filterByParams('IsMarkedDown', (data, param)=>{
return data.filter((obj)=>{ obj[param] === 1 })
})
/* И так далее */
.get(); // В конце get
Поддержи Xakplant
Я давно хочу развить видеоверсию, но пока этого не получается из-за нехватки ресурсов. Сейчас я собираю деньги на новый компьютер и микрофон. Поддержи xaklant и ты увидишь полезные видео быстрее.