Creating Modular Table with Sort and Filter Features in React

Daniel Pericich
7 min readJan 21, 2021

Whether you are managing user records, or looking at product stats, being able to create and format a good table in React is a must. HTML makes creating a basic table easy, but with every table comes the expectation of functionality such as sorting columns by order and filtering out data. In this article I will walk you through how to create modular components and a base table layout for your next project or assignment.

Anyone who knows me knows that I am a sports stat junkie. I love to compare performances on a week to week basis between teams. This may look like comparing turnover differentials between football teams, or tracking the usage or targets for a certain player. While ESPN and NFL offer all the stats you could want, there is a lot of moving around needed to get the right numbers on your screen. Because of this, I decided to build a customized NFL dashboard using a self-built modular component library for tables and filters, and Plotly.js for graphing.

As I work through this project, I decided to write articles to help out people working on their own projects, front end engineers who may be new to their data teams, or hobbyists looking for a better way to display and analyze their data. If you have questions or thoughts, please leave a comment and like below!

General Layout

For this table there are two modular components that we need to create, then we will connect them in a larger layout component. This layout component will both handle the arrangement of our elements on the screen as well as mange the table state. The goal of making the dropdown and table components modular is to allow for anyone to easily configure their own layout with as many dropdowns and as large of data columns and row as they want.

Below is a Figma sketch of the table that we will be building:

Figure 1. Figma mockup of our modular components and table layout component

There is a title (not a component, just a styled element), two drop downs for filtering by team and position and the table which will display all the information from the object passed into our table layout component by the App itself.

This is a specific use case for the information we would like to work with, but by building modular components, we are able to build unique tables with as many filter dropdowns as we want. All we have to do is call the extra dropdowns in our template and pass in the appropriate arguments.

** Though I have done extensive custom CSS for this table and app, I will not be reviewing it in the articles. Check out my Github to view the source code.**

How to Handle React State

My first instinct for handling state with these features was to use hooks. Using hooks would allow us to store the state of the selected value for the dropdowns, or the sorted table for our columns directly in the components in which they are called. After more thought I backed away from this approach as each component would be responsible for the state of its dropdown option and the current configuration of the table.

The layout was what made me reconsider as there is a lot more opportunity for the table state to be out of order if each component modifies its own component state directly. To avoid tracking the separate state of many components, we will build the individual components to communicate their values with the layout component which can then update a central state.

The state for our table layout is the following:

Figure 2. TeamDataTable layout component state

Instead of storing and updating a specific piece of state for each dropdown filter, I opted to create a callback function that updates the table state directly. Each dropdown needs a slightly different callback function to correctly grab and update state, but this approach simplifies the amount of state needed to update for every filter. Like magic, we go from needing to create key/value pairs of position/string and team/string to modifying and returning the players state value in a single action. Much better!

The sorting state is a little bit more challenging. As I said before, I really wanted to try to use hooks when I first started working with this app, but ran into a number of issues. The most important one was the unidirectional nature of the sorting. My first configuration allowed you to sort on column click, but did not allow you to toggle sort. Because of this, you could only sort in ascending or descending order because the only state being stored in the hook was the modified state itself. You can see my first attempt here:

Figure 3. Using Hooks for Table State

To solve this problem, I created a nested sort state object. This object holds a number of Boolean objects attached to names for each of the columns. Depending on which column is clicked, the callback onClick function that is passed to the column headers will sort the values in ascending or descending order.

The final, but main part of our state is the table data itself. We will receive the initial table data as an array of objects and store this in our layout components state. Once we have connected to the Shield API, we will be making an API call on our main app component’s componentDidMount lifecycle, but for this article, we will just use a statically created object passed as a prop from App.js to TeamDataTable.js.

Creating the Dropdown for Filtering

Let’s look at the easier component first, the filter dropdown. As mentioned earlier, the only piece of information that our layout needs from this is the currently selected option value. Below is a snapshot of our component:

Figure 4. FilterDropDown modular component code

To make our component modular, we accept three props: items, value and getValue. Items is a copy of our table data, value is the specific column that we want to filter and getValue is a callback function to update the state of our layout component whenever we change the option value of our dropdown. This component will work for any column that we wish to filter as long as we pass in the three props.

Creating the Table

The table is more involved. The component itself is not necessarily complicated, but the nested state for sorting its columns makes it a little more difficult. Below is a snapshot of the component:

Figure 5. Table modular component code

Here we create a basic html table based off the object array from our layout state. The two items passed to this component are items, the array of objects for populating our table, and sortColumn which is a callback function used to update state. We create the headers outside of the return function and then map over the table data itself to create the rows of data.

When we are creating the headers we add an onClick even handler to the table headers to allow us to toggle the state of each column in our layout component. That concludes are table component.

Putting it all Together

With our filter dropdown component and table primed and ready for props, now we just need to handle the props being passed in. Both components are reliant on our updated players object stored within the TeamDataTable.js component. This state is first set with the props passed down from App.js and then updated by the filter dropdown callbacks updatePosition and updateTeam. These two filter callbacks check the current select value and based on the value, filter out nonmatching values (check the source code on my Github).

To manage the sort headers, we receive the table head’s value/title and are then able to access the nested object inside our state’s sort object. From here we check if the value is true or false, and sort by the table’s value before updating the players state, rerendering our table and finally toggling the column boolean.

Creating a table with sorting and filtering is not easy, but is a must for any frontend developer serious about contributing to their teams data platform, or looking to get better at state in general. If you have any thoughts on this article, or ideas for making an even better table, please leave a comment below! Keep your eyes out for the next article in this series which will focus on working with Plotly.js to bring your data to life with graphing!

--

--

Daniel Pericich

Former Big Beer Engineer turned Full Stack Software Engineer