I've been exploring how to implement filters in React, this is my approach to it.
What is this trying to solve?
- Provide a defined interface for declaring filters and getting state of them at any given time.
- It should work with any UI.
- It doesn't know about the data source(API, local, etc).
- Leave the responsability of applying the filters to the consumer, our
Filters
component only takes care of keeping up to date each of the filters - Only the consumer knows what each filter "means".
<Filters onChange={this.onFilterChange}>
<div>
<span>Title</span>
<InputFilter filterName="title" />
</div>
<SelectFilter filterName="year" />
</Filters>
It injects an updateFilter: func(name, value)
function through the context to the 'filter' components.
onChange: func({ filters })
: It is called every time a filter changes and it receives the state of the filters as a param.{ filters: { title: 'Frozen', year: '2013' } }
For this example I created an InputFilter
, but you could imagine the same pattern working with a SelectFilter
, RadioFilter
, etc. It calls updateFilter
everytime the value of the input changes.
NOTE: We could add a debounce
when the user types insice the InputFilter
and no other component or interface should be affected.
A higher-order component that injects the updateFilter
prop to a component and partially applies it so that the composed component doesn't have to care about the name of the filter.
We could modify this to work with nested filters, which means that we could reuse a whole set of filters
.
const PageFilters = () => (
<Filters>
<NumberFilter filterName={pageSize} />
</Filters>
);
// Then we can reuse this...
<Filters onChange={this.onFilterChange}>
<div>
<span>Title</span>
<InputFilter filterName="title" />
</div>
<PageFilters filterName="page" />
</Filters>
//Whe onChange gets called it will receive something like `{ filters: { title: 'The' }, page: { pageSize: 20 } }`
<Filter filterName="hasClicked">
{(updateFilter) => (
<div onClick={() => updateFilter(true)}>Click me</div>
)}
</Filter>
@rogeliog Interesting approach. I wonder how you'd update it with Context. I might post a response when I'm done building something similar.