0.4.1 • Published 5 years ago

redux-filterlist v0.4.1

Weekly downloads
8
License
MIT
Repository
github
Last release
5 years ago

NPM

deprecation notice

redux-filterlist is depreacted. I recommend to use https://github.com/vtaits/react-filterlist instead

redux-filterlist

A Higher Order Component using react-redux for creating lists with filters, sotring, paginatinon, endless scroll etc.

Installation

npm install redux-filterlist --save

or

yarn add redux-filterlist

This package requiers next packages: react, redux, react-redux. Easy way to install:

npm install react redux react-redux --save

or

yarn add react redux react-redux

And next polyfills:

  • Promise
  • Array.prototype.includes
  • regeneratorRuntime

Examples are here.

Api

reduxFilterlist

reduxFilterlist is a decorator that provides list state and actions to component.

import {reduxFilterlist} from 'redux-filterlist'

reduxFilterlist({
  ...params,
})(List)

Params:

NameRequiredTypeDescription
listIdtrueString or Numberthe name of list and the key to where list's state will be mounted under the redux-filterlist reducer
loadItemstrueasync Functionshould return Object { items: / Array of loaded data /, additional: {} / Additional info (total count etc.), can be null if not needed / } or throw LoadListError with Object { error / any, can be null if not needed /, additional }
additionalfalseanyAdditional info (total count etc.) setted by default
sortfalseObjectdefault sorting state of the list, should be an Object { param / string, column id / , asc / boolean, asc or desc / }
isDefaultSortAscfalseBooleandefault asc param after change sorting column (true by default)
appliedFiltersfalseObjectfilters and their values that applied by default. Should be { filterName1: filterValue, filter2Name: filter2Value, ... }
initialFiltersfalseObjectfilters and their values that sets after filter reset. Should be { filterName1: filterValue, filter2Name: filter2Value, ... }
alwaysResetFiltersfalseObjectfilters and their values that sets after every filters or sorting change. Should be { filterName1: filterValue, filter2Name: filter2Value, ... }
saveFiltersOnResetAllfalseArrayfilters names that not reset after resetAllFilters call. Should be filterName1, filter2Name, ...
saveItemsWhileLoadfalseBooleanby default items are cleared if filters or sorting changed. If saveItemsWhileLoad is true, previous list items are saved while load request is pending
onBeforeRequestfalse(listState, props, actionType) => voidhook that called before each items request. See below
autoloadfalseBooleanconfigure initial loading process
getStateFromPropsfalse(componentProps: Object) => { filters?: Object; appliedFilters?: Object; sort?: { param?: string; asc: boolean } }changes filters, applied filters and sorting on init and props change. E.g. if list should change own state on location change
shouldRecountStatefalse(nextComponentProps: Object, prevComponentProps: Object) => booleanif defined, getStateFromProps will be called on component props change only if shouldRecountState returned true

All params except for listId can be redefined with component props.

List state

ParamDescriptionType
loadingis list loading in this momentBoolean
itemsloaded itemsArray
additionaladditional info that can be recieved together with itemsany
errorerror that can be received if list not loadedany
sortsorting state of the listObject { param, asc }
filterscurrent filters state on page (intermediate inputs values etc.)Object { filterName1: filterValue, filter2Name: filter2Value, ... }
appliedFiltersapplied filtersObject { filterName1: filterValue, filter2Name: filter2Value, ... }
isDefaultSortAscparam from decoratorBoolean
initialFiltersparam from decoratorObject
alwaysResetFiltersparam from decoratorObject
saveFiltersOnResetAllparam from decoratorArray
requestIdinternalInteger

Component props

PropertyTypeArgumentsDescription
listIdString or Numberparam from decorator
listStateObjectcurrent state of list
loadItemsFunctionloads more items to page
setFilterValueFunctionfilterName, valuesets filter intermediate value
applyFilterFunctionfilterNameapplies filter intermediate value, clears list and loads items
setAndApplyFilterFunctionfilterName, valuesets filter values, applies that, clears list and loads items
resetFilterFunctionfilterNameresets filter value to it initial value, applies that, clears list and loads items
setFiltersValuesFunctionObject { filterName1: filterValue, filter2Name: filter2Value, ... }sets multiple filters intermediate values
applyFiltersFunctionArray filterName1, filter2Name, ...applies multiple filters intermediate values, clears list and loads items
setAndApplyFiltersFunctionObject { filterName1: filterValue, filter2Name: filter2Value, ... }sets multiple filters values, applies them, clears list and loads items
resetFiltersFunctionArray filterName1, filter2Name, ...resets filters values to them initial values, applies them, clears list and loads items
resetAllFiltersFunctionresets all filters (without saveFiltersOnResetAll) values to them initial values, applies them, clears list and loads items
setSortingFunctionparam, ascsets sorting column. If asc defined and Boolean, sets it. Otherwise, if this column differs from previous sorting column, asc will be setted with isDefaultSortAsc param from decorator. Otherwise, it will be reverse asc param from previous state.
resetSortingFunctionresets sorting. Sort param will be setted with null, asc will be setted with isDefaultSortAsc param from decorator.
deleteItemFunctionindex, additionaldelete item with specified index from list. If additional defined, sets it.
updateItemFunctionindex, item, additionalupdate item by specified index. If additional defined, sets it.

reducer

Stores lists states.

import {reducer as reduxFilterlistReducer} from 'redux-filterlist'

Should be mounted to reduxFilterlist in root reducer.

const reducers = combineReducers({
  ...otherReducers,
  reduxFilterlist: reduxFilterlistReducer,
})

const store = createStoreWithMiddleware(reducers)

reducer.plugin

Returns a list reducer that will also pass each action through additional reducers specified. In first argument takes an object with keys is lists ids and values is additional reducers that calls after each action dispatch if list mounted.

Should be mounted to reduxFilterlist in root reducer instead of original reducer.

const reducers = combineReducers({
  ...otherReducers,
  reduxFilterlist: reduxFilterlistReducer.plugin({
    pluginList: (state, {type, payload}) => {
      switch (type) {
        case CHECK:
          return {
            ...state,
            items: state.items.map((car) => {
              if (car.id === payload.carId) {
                return {
                  ...car,
                  checked: payload.checked,
                }
              }

              return car
            }),
          }

        default:
          return state
      }
    },
  }),
})

const store = createStoreWithMiddleware(reducers)

filterlistPropTypes

PropTypes of decorated component.

import {reduxFilterlist, filterlistPropTypes} from 'redux-filterlist'

const List = (props) => {
  ...
}

List.propTypes = {
  ...filterlistPropTypes({}),
  ...otherPropTypes,
}

reduxFilterlist({
  ...params,
})(List)

Customization of list PropTypes

import {reduxFilterlist, filterlistPropTypes} from 'redux-filterlist'
import PropTypes from 'prop-types';

const List = (props) => {
  ...
}

List.propTypes = {
  ...filterlistPropTypes({
    // PropTypes for all items from list state
    // PropTypes.any by default
    item: PropTypes.shape({
      id: PropTypes.number.isRequired,
      name: PropTypes.string.isRequired,
    }),

    // PropTypes for `additional` from list state
    // PropTypes.any by default
    additional: PropTypes.shape({
      count: PropTypes.number.isRequired,
    }),

    // PropTypes for `error` from list state
    // PropTypes.any by default
    error: PropTypes.shape({
      status: PropTypes.oneOf([
        403,
        404,
      ]).isRequired,

      message: PropTypes.string.isRequired,
    }),

    // PropTypes from `filters` and `appliedFilters` from list state
    // PropTypes.object by default
    filters: PropTypes.shape({
      page: PropTypes.number.isRequired,
      pageSize: PropTypes.number.isRequired,
      query: PropTypes.string,
    }),
  }),
  ...otherPropTypes,
}

reduxFilterlist({
  ...params,
})(List)

onBeforeRequest

Hook that called before each items request. Recives next arguments:

  1. state of current list
  2. props of component
  3. type of change list action

Action types:

  • setStateFromProps
  • setAndApplyFilter
  • setFilterValue
  • setFiltersValues
  • setAndApplyFilters
  • setSorting
  • resetSorting
  • resetAllFilters
  • resetFilters
  • applyFilters
  • resetFilter
  • applyFilter
  • loadItems
  • loadItemsOnInit

Testing

For unit testing you can export component and decorated component separately, e.g.

import { reduxFilterlist } from 'redux-filterlist';

export class ListComponent extends Component {
  ...
}

export default reduxFilterlist({
  ...params
})(ListComponent);

Then, in you test file you can simulate various states using filterlistProps.

import { shallow } from 'enzyme';

import { filterlistProps } from 'redux-filterlist/lib/fixtures'; // <---------

import { ListComponent } from '../list';

test('should render without crash', () => {
  shallow(
    <ListComponent
      {...filterlistProps}
    />
  );
});

test('should render preloader in loading state', () => {
  const wrapper = shallow(
    <ListComponent
      {...filterlistProps}
      listState={{
        ...filterlistProps.listState,
        loading: true,
      }}
    />
  );

  // check if preloader is rendered
});

test('should render items', () => {
  const wrapper = shallow(
    <ListComponent
      {...filterlistProps}
      listState={{
        ...filterlistProps.listState,
        items: [
          // items of list
        ],
      }}
    />
  );

  // check rendered items
});

Getting Started

Step #1

Connect redux-filterlist reducer.

import { createStore, combineReducers } from 'redux'
import { reducer as reduxFilterlistReducer } from 'redux-filterlist'

const reducers = {
  // ... your other reducers here ...
  reduxFilterlist: reduxFilterlistReducer,
}

const reducer = combineReducers(reducers)
const store = createStore(reducer)

Step #2

Decorate your list component with reduxFilterlist(). This will provide your component with props that provide information about list state and functions to filtration, sorting, loading and others.

import React from 'react'

import { reduxFilterlist } from 'redux-filterlist'

const List = ({
  listState: {
    additional,
    items,
    loading,
  },
}) => (
  <div>
    <table>
      <thead>
        <tr>
          <th>id</th>
          <th>brand</th>
          <th>owner</th>
          <th>color</th>
        </tr>
      </thead>

      <tbody>
        {
          items.map(({
            id,
            brand,
            owner,
            color,
          }) => (
            <tr key={ id }>
              <td>{ id }</td>
              <td>{ brand }</td>
              <td>{ owner }</td>
              <td>{ color }</td>
            </tr>
          ))
        }
      </tbody>
    </table>

    {
      additional && (
        <h4>
          Total: { additional.count }
        </h4>
      )
    }

    {
      loading && (
        <h3>Loading...</h3>
      )
    }
  </div>
)

/*
 * assuming the API returns something like this:
 *   const json = [
 *     {
 *       id: 1,
 *       brand: 'Audi',
 *       owner: 'Tom',
 *       color: 'yellow',
 *     },
 *     {
 *       id: 2,
 *       brand: 'Mercedes',
 *       owner: 'Henry',
 *       color: 'white',
 *     },
 *     {
 *       id: 3,
 *       brand: 'BMW',
 *       owner: 'Alex',
 *       color: 'black',
 *     },
 *   ]
 */

export default reduxFilterlist({
  listId: 'simple',
  loadItems: () => fetch('/cars')
    .then((response) => response.json())
    .then((cars) => ({
      items: cars,
      additional: {
        count: cars.length,
      },
    })),
})(List)

Done

An example here.

0.4.1

5 years ago

0.4.0

6 years ago

0.3.2

6 years ago

0.3.1

6 years ago

0.3.0

6 years ago

0.2.6

6 years ago

0.2.5

6 years ago

0.2.4

6 years ago

0.2.3

7 years ago

0.2.2

7 years ago

0.2.1

7 years ago

0.2.0

7 years ago

0.1.4

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago