Crater_Lake_National_Park_Oregon
ExploreOurteamtrainingCourses (3)

Most web applications give people an interface to interact with and explore data. Thus, a core challenge of building applications is continuously and reliably syncing the data with the user interface. Toggled switches should stay toggled. Checked boxes should stay checked. Pushing the ‘like’ button should change that button to ‘liked’. The right data should be showing at the right time. This all fits into the realm of managing ‘state’.

In simple apps, vanilla JavaScript or jQuery generally work fine for managing state. But as applications grow in size and complexity, JS/jQuery can turn into spaghetti code. It will become much harder to know which code is affecting which part of the state. This can create bugs for users and suffering for the developers who have to fix those bugs.

JavaScript libraries and frameworks have been evolving quickly to make applications more reliable and easy to debug. This state management problem is getting easier. React and Redux are two libraries (often used together) that have gained a lot of use in the last 2 years. This tutorial will cover the process of converting a jQuery app to React/Redux.

Explore React Courses

In our prior post about using JQuery and Google Maps to display and filter campgrounds, we learned how to leverage GeoJSON properties to control which markers are displayed on the map, allowing us to see only a subset of campgrounds that meet our criteria.

screen-shot-2017-01-06-at-5-24-00-pm

While this is a fine way to design front end filtering, we can improve upon it using React and Redux to help us make our implementation more DRY than WET. In this post we will learn how to build a campground filtering interface using React/Redux by exploring how this architecture can improve on our prior JQuery implementation. Here are some benefits of making the switch:

  • To add a new filter option in our JQuery implementation, we would have to manually add a new filter button to the UI and hook up event handlers. With React/Redux, we can define the filters we want at runtime and let React/Redux handle the UI and event updates for us.
  • By using state we can avoid having to query the DOM or maintain global variables to keep track of what filters are currently in use
  • React will only redraw elements of the UI which have been altered by changes in state, resulting in faster rendering.
  • Using React components, we can compartmentalize different parts of the UI into separate classes, enabling us to easily update and add new components without having to touch the entire codebase.

In this post we will learn how to create the filtering used in this demo app for finding campgrounds near Crater Lake using React, Redux, and react-bootstrap (for simple styling). You can see the complete code on Github.

screen-shot-2017-01-06-at-5-25-20-pm

The React Components

Lets get started by going over the React components we will use to filter campgrounds. Our top level component, CampFilterList, will create items of type CampFilter from a list of filters passed down via props. Taking a look at CampFilterList.jsx:

For each item in the filters list we create a CampFilter component with the id that will be passed back to the changeFilter method, which we will discuss in the Redux section. Taking a look at the CampFilter component, we can see how the changeFilter method is called on click, and the id sent as its parameter.

When a filter is checked, the changeFilter method is called back through the store to the reducer, changing the inuse field, resulting in the campgrounds list being filtered. Like the filters the campgrounds components are list/item, but don’t have any user interaction. You can check out the details in CampList.jsx and CampListItem.jsx.

Setting up the Redux store

Now that we have our filtering React components, let’s go over how we will use Redux to manage state and do the campground filtering.

First, we need to define the initial state for our app in index.js:

In the set_state function we define a list of filters to be used for our app.  The “inuse” field will keep track of how the user is filtering items in the UI. We also define a list of campgrounds that we will apply the filters to. The campgrounds are read from a geoJSON file with properties relating back to the filters:

You can see how the properties map back to the filter IDs, for example, the ‘pets’ property in the geoJSON corresponds to the filter with ID ‘pets’.

The store variable in the set_state function refers to the Redux store, which will handle the state updates for our app. To create a Redux store we need to have a reducer to handle state transition. Let’s add the SET_STATE action to the reducer in reducer.js, enabling us to execute the above code:

We are using the immutable.js library, so in the setState function we can simply merge the new state with the old without having to add additional code to avoid mutating the state when updating.

We will also need a method for updating the filters based on the user’s interactions, so let’s add that to the reducer as well:

The CHANGE_FILTER action will occur when a checkbox is clicked, so our changeFilter implementation should 1. Identify which member of the filter list initiated the CHANGE_FILTER action and 2. Toggle the value of that member’s inuse property and 3. Return the updated filter values as part of the new state:

With the SET_STATE and CHANGE_FILTER actions described in the reducer, we can now create the store used to dispatch our initial SET_STATE command to in index.js:

Where get_campgrounds is the function that converts the geoJSON features to a list of objects for the campgrounds state element referenced in the set_state function.

We can now render our app, passing the Redux store as the Provider for the app. This enables the app to interact with the Redux dispatcher to process state changes:

With the Redux dispatcher set up, we can now move on to creating the app container, where we will map the actions for our app back to the dispatcher and set the props to be used by the app.

The action_creators.js file is our link between the delegate props that will be sent to the children components and the Redux store. In action_creators.js we define a function to handle filter changes:

The changeFilter function returns an action with parameters type and filter, which might look familiar to you looking back at reducer.js:

In addition to the action creators, we need to map the state from the Redux provider to the props that will be passed to the React components. In addition, we will perform the filtering of the campgrounds in the app container before sending the campgrounds down to the components to get rendered. First, let’s get the active filters, those with inuse true, so we know how to filter the campgrounds. From CampFilterApp.jsx:

Notice that we start by setting filtered_campgrounds to the entire campgrounds list. In the event that none of the filters are selected, such as when we first load the app, we want to pass all the campgrounds to the React components. Next, we iterate through all the active filters, keeping only the campgrounds that have true properties matching the active_filters:

If there are no active filters the filtered_campgrounds list remains unchanged, meaning all campgrounds will be displayed.

Finally, now that we have the list of filtered campgrounds, we return this and the other state information as props:

Phew! We are now ready to create the app container, connecting the action creators and the mapped states to the CampFilterApp component:

Recall that CampFilterAppContainer was referenced in the ReactDOM.render() function in index.js. It’s a little circuitous, so let’s just recap what we went over here:

  1. We created the Redux store based on the methods in reducer.js, which define how changes in state are handled
  2. We created an app container to
    1. stick together the action creators, which define the interface between the components and the reducer, with
    2. the redux store state, which is mapped to properties, resulting in creating the props that will be consumed by the React components.
  3. We render this app container when the app is initialized in index.js

Last but not least, we can now render the app. From CampFilterApp.jsx:

The props that are being passed to the React components will include the filters and filtered_campgrounds defined in index.js (mapped to props in CampFilterApp.jsx) and the changeFilter action defined in action_creators.js.  Recall from our React components above that changeFilter is attached to the filter checkbox onClick event, so our app will now filter the campgrounds.

Give it a try using the demo app on Heroku or download the code and run it for yourself. If you want to add a new filter simply add a new field to the geoJSON features and a corresponding item to the filter state list and let React/Redux take care of the rest!

Explore React Courses

Sev Leonard
Sev (sev@thedatascout.com) is a consultant, writer, and technical trainer with 20 years of industry experience. His areas of expertise include analytics and data science, software development, web, and business consulting. http://www.thedatascout.com
0
jQuery, React

Comments