As one of the most trafficked pages at Redfin, the map page has always been a major driving force behind engaging users with the site and helping people find their dream homes. Redfin was the first company to put homes for sale on a map, and we’ve been working on improving that experience ever since.
Previously, our map was built out using JSP with Dojo on the client side to handle user interactions. For the past year, we have been converting the map to use React and Reflux, which allows us to take advantage of performance benefits through server-side rendering using React Server, our open source framework that we recently announced. This post talks about our setup and how we architected the page to build a single page application using the tools mentioned above.
Thinking About the Map Page in React and Reflux
Reflux is an implementation of Flux for unidirectional data flow composed of stores, views, and actions that handles data, displays data, and initiates data fetching respectively. React fits in as the view layer of the model.Now let’s walk through a typical map experience:
- Some user interaction triggers a search
- XHR request fires off to the server side to grab data about homes
- View layer updates with the new data
By applying the Reflux model, we get an action that defines some user interaction, a store that listens to the action and fires off an XHR request, and a React component that renders the data. This setup benefits greatly from Reflux’s unidirectional data flow architecture which makes the code path clean and easy to manage.
Deep Dive Into Architecture
While figuring out the architecture, we encountered several roadblocks:
- We want to have stores that both retain server data, and govern what the React components look like.
- There are many possible user interactions on the map page and we want to unify them into one code path.
Data and View Stores
Stores are responsible for controlling what the view should display. On the map page, that means the stores are responsible for firing XHR requests, and figuring out complex view logic such as talking to Google Maps API. We chose to split out the responsibilities of the store into data stores and view stores, which effectively separates server data from view data.
Data stores are surprisingly simple with a fetch method. Given some parameters, it builds out a request URL and handles the data from the server.
View stores on the other hand will decide how a component is rendered. The stores can contain business logic that controls whether a component should be hidden, or handle communication with Google Maps. In addition, view stores can be set up to have substores which will provide a hierarchical structure that works nicely with the way React components are built out.
Managing User Interactions
There are so many ways to do a new search on the map! Some examples include typing in a new region on the search box, opening up the filter and adjusting the price, or simply zooming in on the map.
This is where Reflux actions come into play. Any user interaction in the React component will fire off an action to notify the store that something has changed. However, we don’t want the data stores to react (ha!) to all interactions in the view layer. We let the view stores listen to those actions and decide whether we should bubble those to the data stores to fetch new data.
But in the world where data and view stores are separate entities, how should the stores communicate with each other?
Tying it All Together: The Dispatcher
In the world of React and Flux, the dispatcher is a singleton used to “broadcast payloads to registered callbacks.” It acts as the hub for data flow and bridges the gap between action callbacks and stores by propagating server data. We really like the simplicity of Reflux (over Flux) and the idea of a hub that manages data flow between server data and the view layer, so we decided to implement our own “dispatcher”.In our architecture, the dispatcher is the bridge that connects the data and view stores. It enables two-way communication between the stores and is responsible for two things:
- Telling the data store to fetch
- Passing data to the view stores
But wait, that doesn’t look unidirectional does it?
The double-headed arrows may indicate otherwise, but let’s sketch out a typical interaction of a user doing a search by typing in a city name in the search box…By decoupling fetching and rendering, we have effectively extended the Reflux flow to the dispatcher and the data store. By breaking down complex stores into substores, you can imagine a hierarchical structure of substores that binds to corresponding React components. Let’s take an overall look at the benefits and downsides of this setup.
- Decoupling data: server data is not directly tied to the view data. View stores act as a layer of abstraction to build out the data hierarchy to be consumed by the view.
- Managing lower-level actions: views can contain their own actions that their corresponding view stores will manage.
- Managing higher-level actions: only major actions from view components funnel through view stores to the dispatcher.
- Data duplication: server data is used to build a separate set of view data. Although this has negative impacts on performance, we value the flexibility since the same piece of data is consumed by different components that expect different formats.
- Single data mutations: our current setup deals very well with full reflows of the page when we trigger a new search. As a result, single data mutations such as favoriting a home are more cumbersome.
Wrapping it Up
This article outlined some of the interesting challenges we encountered when overhauling our frontend architecture on the map page. The transition from Dojo to React and Reflux on React Server was great! We really enjoyed the much more unified code paths and data flows from a unidirectional setup along with great performance features from React Server. Having a more structured code path has also made it a lot easier for other teams to add features to our page!
I’m not saying that everyone should go back and rewrite their pages. But it is an interesting thought experiment to consider how React and Reflux can be applied to complex pages such as our map page.
Please leave comments below if you have any questions or concerns–I hope this has been helpful!