Building a react based application
Building a frontend application is hard. Compared to building backend applications, you have way too much mutable state to manage and in the end your whole code base is so complex that nobody wants to touch anything.
We already know how to build backend applications - generating html, sending it to the browser, repeat - without it ending in a nightmare of complexity, but building frontend stuff is very complicated. Backend is easy, frontend is hard. So, why don’t we just build the frontend like the backend?
Building it like a backend application
A good old web application usually consists of a router mapping the current url to a controller action which will render the whole page including the outer layout. The big picture looks like this:
By replicating this cycle in our frontend application we can - together with react - reach an easier to understand application design with less mutable parts.
Implementing it
When the user is navigating to a different view, we will just use links. When the user clicks a link an event handler will tell the router to render the new page (by using window.history.pushState
). Our frontend application flow now looks like this:
This leads to a one-directional data flow which is easier to reason about than a bidirectional data flow which you can usually find in frontend applications.
Let’s take a look to some example code.
The Application Component
We have an Application
component which will intercept window.history.pushState
calls to rerender the page when the url changes. The Application
also keeps a user as it’s state. The user is just a plain old javascript object[0]. If the user has some relations (e.g. the user has some blog posts) these relations will be part of the user object, so that you only have one single application data state. So the major mutable parts of the whole application are capsulated in the Application
.
Once the user has logged in Application.state.user
will be set by calling window.history.pushState(user, undefined, undefined)
from the login view. Remember: The first argument of pushState
can be any javascript object.
If a view is updating the user (e.g. account settings) it will not mutate the user object, instead it will create a copy and update the copy. Then it will use window.history.pushState(newUser, undefined, undefined)
to push the updated user object to the Application
[1]. The views always have the latest user object by design, the views cannot get out of sync with the data. Also there is no mutable data, awesome!
The user object is passed down from the Application
to the views by currying the route actions (postAction
in the code below) with the user.
The Link Component
A Link
component will render good old <a href="..">..</a>
elements, but will intercept the onClick
event to use window.history.pushState
instead of doing a real http request.
The Views
You can see that the view modules only export their routes and their url generator functions. So communication across views only happens via Links
or more specifically by routing.
It’s the views responsibility to display the full page including an outer layout. This is just like a view in rails or symfony. The best way to do this is by introducing an OuterLayout
component.
Why not flux
Flux is IMO a great pattern for building hybrid react apps (so not using react everywhere). In case you’re using a pure react based application you don’t need data stores and dispatchers. Data stores and dispatchers lead to mutable state and indirect code. Having multiple data stores also introduces multiple sources of truth, which increases complexity. If your app is only using react for some parts flux is a great solution, but if you are using react for everything you are better of with direct code and immutable data.
Takeaways
By building your react application like a backend application - introducing clearly seperated views, communicating only via urls - you will archieve a simple single directed data flow. By using immutable data structures the only major state (so, ignoring ui state like animation state) is a tuple of the user data structure and the current url.
Thanks for reading :) Follow me on twitter if you’re interested in more of this stuff!
[0] An example user object could look like this: { id: 1, email: 'hello@example.com', posts: [{title: 'My first post', content: 'Lorem ipsum'}] }
.
[1] Usually you do this after sending an ajax request to the server. With promises this could look like this:
where updateEmail
is doing an ajax request and returning a Promise.
Hey! I’m Marc, founder of digitally induced. I love playing with exciting new technology. Feel free to contact me.
If you liked this post feel free to subscribe to my mailing list.