This is an isomorphic/universal example built with react and Redux
which utilizes the same codebase on server and browser to correctly handle multiple requests at the same time.
This example was previously built with alt and now fully migrated to redux
, you might as well compare the two and see the differences.
Feel free to ask questions or send over pull requests.
$ npm i
$ npm start
Then visit localhost:3000
.
- It is highly recommented to always remove
node_modules
and reinstall everything to avoid any possible issues
We love react and flux for that it provides a smooth workflow and much simpler mindset to develop applications.
On top of that, redux makes things like 10x easier by transforming flux store
into pure functions, with no state stored inside, which solved a lot of singleton
and waiting
issues in other implementations, it also encourages a lot of best practices like container component
, easy extension points and minimal API surface area.
Under the hood, redux is heavily based on various functional programming
ideas to make those nice things happen, but don't let the functional aspect of redux scares you off, as a lib user, you don't really need to know about those inner workings to get started
.
That being said, it's quite an enjoyable and amazing experience to read through the whole codebase line by line and fully comprehend how things were designed to work in a very different manner, it's truly an eye opener.
In short, if you could only pick up one new skill this summer (2015), redux
should be on top of your list (and a bit of functional programming knowledge won't hurt too :)
-
React, Redux, universal/isomorphic, ES6/7, webpack, babel and all those usual suspects!
-
Uses
Promise middleware
for redux to showcase async data fetching along with optimistic updating for each REST API calls -
Uses
createReducer
to greatly simplifyreducers
code by usingconstant mapping
-
Showcase async data fetching solutions with
react-router
before displaying each view (hence no blank screen while waiting for data to be downloaded) -
Showcase how to easily implement
universal
application withredux
andreact-router
on the server (and dehydrate the data on the client too)
-
Reuse the same codebase on both the browser and server without any modification
-
Must be able to handle multiple requests on the server at the same time without polluting each user's data
-
The approach must be easily understandable and simple to develop and maintain so it's strongly fool-proof in a team environment
-
The application can be used in either
universal
(a.k.a server-rendering) orpure client
mode
-
Shorter time-to-first-page means better user experience
-
Deep linking into the application for better usability (ie: bookmarks)
-
SEO (of course!)
An early implementation of this example handles the data model wrong by mutating the state passed into each reducer hence causing a lot of strange issues like making redux-devtools
acting funny.
@gaearon, author of both redux
and redux-devtools
, was kindly enough to pointed out those mistakes and provided detailed instructions on how to make it right.
Later the example was migrated to use immutablejs
but the immutability
spirit stays the same.
- We want to reuse all routing rules on both the client and server, no dupe works needed
- We need to make sure data for each view is fully fetched before rendering to string, this is tricky because most REST API calls are async
- With redux and react-router in place, all these can be done easily and gracefully with
Router.run
, checkoutserver.js
.
Immutable.js
was picked for the job for two main reasons:
-
Immutable source of truth (data model) is easy to reason about, it makes dirty diff fast and the overal app state less error-prone.
-
The data structure itself is fully fool-proof, meaning no one can accidentally modify the data model and causing bugs that are extremely hard to trace and fix, especially in a large team.
Pay special attention to the use of Immutable.Record
data structure and update()
method used to manipulate data.
In most redux applications <Connector>
is the root container component that holds all child components, it's responsible for fetching application state from outside, stores it, monitor it's change
event and triggers redraw.
There's a nice select
function in <Connector>
that you could use to cherry pick and monitor only those reducers
you are interested, by doing so you avoided unnecessary redraw calls hence making the app more performant.
With redux-devtools in place, you can undo/redo changes anytime, just like travelling back and forth in time, it's super neat being able to debug the application at different status.
Pro hint, it can be toggled off by setting window.$REDUX_DEVTOOL = false;
in boot.js
.
Thanks @gaearon for code review and pointing out misc. issues. Lee Byron for bringing immutable.js
to the world, it makes everything easier, you guys rock!
MIT