Sunday, February 8, 2015

Isomorphic React + Flux using Yahoo's Fluxible, part 2

On my last post, I demonstrated how to create a simple Isomorphic Application with multiple routes using Yahoo's Fluxible library. On this post, I will expand that app so it can communicate with a backend API, in order to build a simple To-Do application.

** All the source code for this post is available at this repo. Each commit corresponds to a new section on this post.


We'll start by creating a CRUD service for our Todos.

As you can see, this is a very basic service that just stores todos as an array on memory. However, we use a setTimeout on every action to simulate we're calling a database.

In order to get our app to use the todos service, we'll use Yahoo's fluxible-plugin-fetchr. Fetchr allows us to consume a service from both the server and the client without duplicating code.

We need to make some changes on app.js and server.js

It's important to note that Fetchr expects our app to be using body-parser (or an alternative middleware that populates req.body) before we use the fetchr middleware.

Now we need to define a showTodos action to fetch our todos.

Notice how our action takes a context parameter as its first argument (line 7). This context has a dispatch method that we use to notify our stores (lines 8, 12, and 16). It also has a service attribute, which allows us to interact with any service that has been registered with our app. On line 10 we execute a read() action on our todo service, in order to fetch all todos.

Since we want todos to be displayed on the home page, we'll need to make sure the showTodos action runs every time the user visits the home page. This is very simple, we just need to declare it on routes.js.

After the todos have been fetched, we need to save them on a store so they're available to the rest of the application. Let's create a TodoStore for this.

And now all we're missing is modifying our Home.jsx component so it reads todos from the TodoStore and then renders them.

Notice we're getting the component's initial state by asking the TodoStore for all todos (lines 17-21). We're also using the FluxibleMixin to listen to changes on the TodoStore and calling the _onChange() method appropiately.

Our app should now be able to fetch all todos and render them on the home screen. However, this might be a good time to review our now expanded request/response cycle:

  1. A new request is made, server.js receives it.
  2. Our middleware creates a new context instance and calls context.executeAction(navigateAction), passing it the current route.
  3. navigateAction uses the routrPlugin to look for a matching route (home). Since we defined the showTodosAction as an action for the home route, the showTodosAction is executed.
  4. The showTodosAction uses the fetchr plugin to make a 'read' request on the todos service.
  5. After the read request succeeds, the showTodosAction dispatches a 'RECEIVE_TODOS_SUCCESS' action, with the todos that were returned.
  6. The 'RECEIVE_TODOS_SUCCESS' action is dispatched to all stores registered with the app.
  7. The TodoStore executes its _receiveTodos() method in response to the 'RECEIVE_TODOS_SUCCESS', and updates its local copy of todos.
  8. The showTodosAction calls its callback function, letting the NavigateAction know it can continue.
  9. The navigateAction emits the 'CHANGE_ROUTE_SUCCESS' action, which is dispatched to all stores registered with the app.
  10. ApplicationStore executes its handleNavigate() method in response to the 'CHANGE_ROUTE_SUCCESS' action, and updates its state.
  11. Inside the executeAction callback, we create a new instance of our Application component, passing it the current context as a prop.
  12. We render the Application component as a string, and send the result as our response.

Notice: when we visit the About page, and then click on the Home page, an AJAX request will be made to read from the todos service.

Add Todos

We can now read existing todos, but we're missing the ability to add new ones. Let's fix that by adding a createTodo action.

The createTodo action is pretty similar to our previous showTodos action. The main difference is that we're now calling service.create instead of (line 18), and we're passing along our todo object as the second argument.

Since the TodoStore is not modified directly by the createTodo action, but instead needs to react to its events, we must define the corresponding handlers.

With everything in place, we can now add an input field on Home.jsx that calls createTodo when submitted.


We now have a working Isomorphic application that communicates with a CRUD service. Even though the Update and Delete actions were not implemented, doing so should be pretty trivial now, and is left as an exercise for the reader.

Thanks for reading this 2-part series on building Isomorphic apps with Fluxible, hope you enjoyed it!