React - Filtering


In this final video in this series we are going to add in Filtering functionality to our React CRUD application.

Again, much like in previous videos in both the React and Angular parts of this series, we are going to pass a query parameter to our API request which will handle the filtering for us, returning the results that match.

I want to add in a warning here that this filtering function is very basic / naive. I would strongly advise you to implement more robust logic (perhaps using something like Elasticsearch or similar) in a production setup.

As mentioned, to make this work we must pass an additional parameter via the URL to carry out our filtering. This means when making requests to our API, the URL will look something like:

http://api.symfony-3.dev/app_dev.php/posts?filter=as

We already added in the logic to handle this in the video on sorting, but let's quickly recap:

// /src/actions/blogPostActions.js

import fetch from 'isomorphic-fetch';

export function fetchBlogPosts(page, limit, filter, sort, direction) {

    let p = new URLSearchParams();

    p.append('page', page || 1);
    p.append('limit', limit || 10);
    p.append('filter', filter || '');
    p.append('sort', sort || '');
    p.append('direction', direction || '');

    return fetch('http://api.symfony-3.dev/app_dev.php/posts?' + p, {
        method: 'GET',
        mode: 'CORS'
      })
      .then(res => res.json())
      .catch(err => err);
}

By making use of URLSearchParams we can easily create a URL with all parameters added as needed. We are using the passed in value, or setting a default if a value wasn't passed in, e.g.:

p.append('filter', filter || '');

If we pass in a filter argument when calling fetchBlogPosts then this will be used, otherwise we default (|| which means or) to an empty string ''.

Accepting Input

As we want to filter the available records we need a way to take some user input.

To do this we can add an input element at the top of the table column we'd like to offer filtering on:

// /src/components/Table.js

    render() {
        return (
            <div>
                // * snip *

                <table className="table table-hover table-responsive">
                    <thead>
                    <tr>
                        <th onClick={() => this.sortingHandler('bp.id')}>id</th>
                        <th>
                            <span onClick={() => this.sortingHandler('bp.title')}>Title</span>
                            <input type="text"
                                   id="table_blog_title_filter"
                                   value={this.state.titleFilter}
                                   onChange={this.titleFilterHandler.bind(this)}
                                   className="form-control"/>
                        </th>
                        <th>Options</th>
                    </tr>
                    </thead>
                    <tbody>

This is very similar to how we implemented the limit functionality.

We don't yet have a key on our state called titleFilter, so let's add one in and set it to have a default value of an empty string - we don't want to filter by default:

// /src/components/Table.js

    constructor(props) {
        super(props);

        this.state = {
            reverse: false,
            titleFilter: ''
        };
    }

And also we need to create the titleFilterHandler which we have said should be called whenever an onChange event is triggered:

// /src/components/Table.js

    titleFilterHandler(e) {
        this.setState({
            titleFilter: e.target.value
        });
        this.props.onFilter(e.target.value || '');
    }

Again, we could refactor this to use an ES6 arrow function, as we covered in the previous video.

Passing props

We've told our titleFilterHandler to pass the value from the input (the e.target.value) as the argument to the function passed to this component via the onFilter property (prop).

We haven't yet declared that function, and we must do so in the component that uses this Table component. This would be our list component.

If you are unsure on this, please watch the previous videos in the React part of this tutorial where we have covered this in greater depth.

// /src/containers/list.js

    onFilter(filterBy) {
        this.setState({ filterBy });
        this.getBlogPosts(this.state.currentPageNumber, this.state.limit, filterBy);
    }

    render() {
        let totalPages = Math.ceil(this.state.totalItems / this.state.numItemsPerPage);
        return (
            <div>
                <Table blogPosts={this.state.blogPosts}
                       onDelete={this.onDelete.bind(this)}
                       onSort={this.onSort.bind(this)}
                       onFilter={this.onFilter.bind(this)}
                       onLimit={this.onLimit.bind(this)}
                ></Table>
            </div>
        );
    }

And that just about wraps up this series.

I hope you have found the concepts in these videos to be useful.

At this point I would say that more than likely, if you are working with React, you will highly likely be bringing Redux into the mix. In future React videos I will be using Redux, aiming to keep these implementations as close to real life as possible.

If there are any parts you are unsure on, please feel free to ask in the video comments for any video. If there is anything you would like to see in a video, or video series, also please leave this in any video comment or contact me using the support form.

Thanks for watching, and see you in the next series.

Code For This Video

Get the code for this video.

Episodes