How to identify and avoid pitfalls associated with URL parameters

How to identify and avoid pitfalls associated with URL parameters

25.Oct.2021

React is a JavaScript library for building highly scalable user interfaces. The React team at Facebook uses Flux, an architecture that helps them build large web applications that scale well. Flux introduces the concept of "dispatcher" which specifies how components interact with one another by defining actions and adding callback functions to handle those actions. When building an application using the Flux architecture, it's helpful to understand what problems may arise because of URL parameters.

I built a simple demo app with React and Flux called "Fluxing Out" (no affiliation with the popular music festival) for this article (link). It has two routes: / #/ , which shows all of our stores on a map, and /Store/:id# , which shows the details of a specific store. In this article, I'll walk you through how to set up stores and routes using React Router, show you how Flux reacts to changes in URL parameters, and explain what problems can arise along the way.

Route matching with React Router

React Router is a powerful library for routing URLs asynchronously in an application built on top of React. We need a router for our Flux app because it's easier to manage parts of your UI from within components when they're not embedded inside each other. By looking at the URL, we should be able to tell which component matches that route internally. In essence, we want something like this:

In order to achieve this without using additional tools, we'll have to construct each route by hand.

Because this is a React tutorial, let's begin by taking a look at the index.js file that defines our routes:

This file specifies how to match certain paths with React Router. If you're not familiar with the way it works, check out the documentation . Since I'm using Browserify for bundling in this example, I'm requiring react/addons-link rather than simply require . If you aren't using Browserify or similar bundler , you don't have to worry about being so verbose in your require statements! You can still use something like var MyComponent = require('./MyComponent') without problems because modern JavaScript interpreters are smart enough to figure things out.

Looking at the code above, we're specifying that for any route starting with /Stores/ , React Router should render the StoreList component. We do this by building a regular expression that looks like /Stores\/([0-9]+)\// and passing it to the pathNames property when defining our routes. [0-9] represents any number in JavaScript, so in effect it matches all numbers and stores them inside an array in our regex object so we can grab the index later. You'll see this in action throughout this article when we create actions and callbacks .

The second route is where things get interesting: it's similar to the one above, except instead of matching anything after Stores/ , it matches anything after /Store/ and stores the index in a separate property on our regex object, also named id . We'll use this information later when making AJAX requests to fetch data from the server.

One more thing to note: I've added a wildcard route at the bottom that matches any path underneath the URL's hostname, denoted by a single * character. This means if someone were to visit https://static.codemy.com/#/Stores/* , React Router would render StoreList because of its nested placement underneath /Stores/ . If you plan on doing anything with dynamic paths in your application, be sure to add this! Without it, all paths will simply render null .

Setting up Flux architecture

Now that we have our routes in place, let's build a StoreList component that dispatches actions to fetch data from the server and shows a list of stores. Since this is a simple example, I'm going to include all of my React components for this post in one file. This is mainly so you can grab the code easily and follow along if needed, but doing so also demonstrates how React works nicely with Flux. You'd be right to assume the "Flux" part comes after these components finish rendering!

After creating a directory called Stores , let's create index.js inside it:

There's some interesting things happening here. First, we're requiring react/addons-link again because Browserify isn't bundling everything correctly. Next, we're requiring our StoreList component at the top.

I've constructed my stores array by mapping over the items I'd like to display in this list. The reason I'm mapping here is because Stores/index will need an object containing all of these properties once it's rendered. This means if I were to pass this object straight into ReactDOM.render without first constructing it inside a map call, React would throw an error saying two objects are trying to reference each other . Since rendering calls should always return a single root element , the only way to avoid this is by creating that object before hand and passing its reference into render as opposed to actually rendering it there.

Taking a look at Store again, you'll notice I'm only passing the store name and address into it. This is because later on, our StoreList component will be rendering each of these stores inside a list item . React doesn't let you pass children as properties , so we'll need to use another method I like to call "prop drilling" -- setting up intermediate components that contain one or more sub-components until everything is eventually rendered into place. We do this by creating yet another component called StoreItem , but first things first:

Creating our actions and dispatching them with Flux

Before writing any code for StoreList , let's first create an action creator that fetches data from the server and returns a constant representing an AJAX request back to /stores/ . Although I'm not using it in this example, you'll learn why I'm naming my action creator "getStores" shortly.

Inside src , create a new directory called actions and add getStores.js inside:

The two most important parts of the above code are the constructor and handleGetStores() . The constructor is what Flux uses to keep track of which store we're dealing with, since we know it will always receive one argument when making calls to dispatch . The handleGetStores function dispatches an AJAX request back to /stores/ using the API's URL endpoint, caches its result in getState (so React can access state updates), then returns a string representing our API call so other components know what has occurred.