Book Recommendations on Node.js and React
Looking for a new book to read this month? Check out this monthly list of 100 books chosen by NYPL staff members on Staff Picks. Choose an age category, choose one or more themes that interest you to further filter the list, and then choose a book or ebook to borrow from NYPL.
Unlike our other front-end projects which were built on top of Sinatra and AngularJS, Staff Picks is the Digital Experience's first project built on Node.js and React. Although the development stack is different, we encountered many issues when developing in Node.js and React that were similar to the issues encountered with Sinatra and AngularJS, as well as plenty of new issues. This post will give a high level overview of our experience with this new stack.
React is awesome, but the learning curve is steep and can be difficult to grasp after months of AngularJS development. Even though React is the view in our Model-View-Controller application structure, we found that using React with the React Router module and a well thought out architecture can create powerful apps.
Inline CSS or SASS Stylesheet?
Writing inline CSS in DOM elements is bad, right? NYPL.org is going through a redesign and there are designs for components that are reused on multiple pages. Let's use the banner component on Staff Picks as an example. Although this component has a specific copy and image for Staff Picks, we should be able to reuse this banner component on another project or page and not worry about the component's default styles. If we want a new color we can simply pass that style as a property to the component.
We could copy over the component markup and the styles or we can bundle up the component into a small project that can be shared. Using React makes the latter approach easy to implement because we can export our component, share it on NPM or another package managing system, and then require it in a new project. With inline CSS, we don't have to worry about how the component itself will look on a different project. We are using Radium to handle inline CSS and media queries but have found it to be troublesome when rendering the application isomorphically. Because of this, we decided to use both inline CSS and SASS in a stylesheet. We are still figuring out the best approach for small component level styles, application level styles, and component sharing.
Isotopes and filters
The book grid and list layouts and the filtering magic is handled by the Isotopes library. It's a great library to use but it's not just a plug-and-play module for React. The documentation is fantastic, and the API is easy to use and configure, but when working with React we have to remember to call it after the components have mounted. This process is identical to using jQuery to write to the DOM and then calling the
on() method on that element for manipulation.
Once the component is mounted and the Isotopes library is initialized, the component listens to event changes on the age category tabs and the filters on the left sidebar and then begins an "AND" filtering on all the selections. This means we want to filter by books that are, for example, in the "Young Adult" category and with a "Techie" filter. As you select more filters, the list of results dwindles down until you get a book or books that apply for all the selected filters.
When selecting a filter you will notice that some filters will grayed out. This lets the user know that the filter is not selectable because we already computed that those two selected filters will not return a result. In other words, when the "Adult" category is selected and "Tales of Courage" is selected, the "Creepy" filter will be grayed out because there are no books that have both "Tales of Courage" and "Creepy" as tags.
Flux and Alt
Flux is difficult to understand, but it helps us separate components while still allowing them to communicate. To briefly explain Flux: It is an architecture that is used with React for unidirectional data flow. The Flux architecture allows us to know where data is being changed and where actions are taken place.
In the Staff Picks application, when an age tab is clicked an Action is created with the selected age data sent. The Dispatcher emits this event to the Store which will update the data and emit a change event. Any component that is listening to this change event will re-render its view based on the data from the Store. In this example, the filters component will receive a new set of filters to display and will render that list depending on the age category that is clicked.
There are many implementations of Flux but we decided to use Alt because of its server side support. We are also actively investigating the best approach to fetch API data asynchronously before rendering the app on the server side. At the moment we are leaning towards using the Iso Alt helper class but are also looking for alternatives.
The front-end team has been using Node.js for some time now but mostly for running npm commands, using modules, and using build tools. Our decision to switch to Node.js and Express from Sinatra has been very helpful. The biggest motivation was to simplify the way our front-end applications generate SEO pages. For our AngularJS projects we wrote SEO pages in ruby for spiders to crawl. But, using both Node.js and React makes creating SEO pages easier because we can use the same programming language and the same code base for the rendered data.
We have used Node.js before, but we are not experts, and we ran into a few issues such as how to route and how to reverse proxy the application. Our application lives on a separate server from the server for nypl.org and it is reverse proxied to a URL on nypl.org. This is a fairly straight forward process but we are also using React Router for the app's routes. This complicates the routing system on top of Express' routing and the reverse proxied server. The application is reverse proxied into
nypl.org/recommendations/staff-picks/ and the server reads that as a request to the home path
'/'. The client side router, however, reads that path as
'/recommendations/staff-picks/'. Because of this difference in routes, there is a variation on the created React Router route instances.
What lies ahead
Staff Picks is not complete yet. We are fixing bugs and implementing best approaches and community standards in our code. The application works well but we want to keep iterating and updating it as we learn more. The biggest issues we are facing at the moment are fetching API data asynchronously, rendering the application with said API data on the server side and not making unnecessary client side API calls, and updating the application's Flux store with new API data. Our next big project is redesigning the nypl.org homepage in Node.js and React and we'll use the technical knowledge from Staff Picks to build a great user experience.