The key principles
The Redux pattern is mainly based on three fundamental principles:
- Single source of truth
The state of your whole application is stored in an object tree within a single store.
- State is read-only
The only way to mutate the state is to emit an action, an object describing what happened.
- Changes are made with pure functions
To specify how the state tree is transformed by actions, you write pure reducers.
How does it look on Tic-Tac-Toe.js?
1. One single store
Everything that can affect UI is saved on store.js. This is what the store looks like:
2. Game.js dispatches actions whenever needed
Let’s see what happens, for instance, when user clicks on the table grid:
3. Reducers receive actions and return new state
4. Render UI when store gets updated
It’s pretty straightforward. Every time the store gets updated it notifies the subscribers so they can update the UI accordingly.
In the above case I’m doing it slightly different compared to store.subscribe(render) in Redux. I’m actually triggering a global event and letting game.js render the callback whenever necessary.
Update: I've changed from global event emitter to store.subscribe approach to simplify the code.
The important thing here is that we stick with the correct data flow. Which brings me to next topic.
Strict unidirectional data flow
Maybe you haven’t noticed, but on previous examples the data flow is always unidirectional. Game.js — the view — dispatches actions, store.js receives and process them with reducers which later triggers 'store:update' so finally we can render the UI updates.
You can read more about Data Flow on redux.js.org.
Ok, we have everything working on the client-side. But how is Tic-Tac-Toe connecting two players?
Implementing the back-end in node.js + socket.io was really simple and that’s one of the coolest things about Redux. Because everything is based on actions, which are by definition simple objects, the only requirement on the Back-end would be: receiving the action and dispatching it to other players connected in the same room/game id.
The front-end side:
Everything starts on the client-side. When the page loads I generate a random ID, which will then be handed to the server.
The back-end side:
It’s all about registering the room and broadcasting the actions:
Design and usability
The game was designed to work in different resolutions. On desktop version the favicon updates according to the player turn, check that out when you start playing. On mobile it’s possible to save the app on home screen, there’s a special logo icon for that.
“Ok, so how can I play the game?”
- Open the demo on any device (except old browsers).
- Copy the id that appears bellow the grid and send to your friend
- Your friend should paste the id in the same field and click on the refresh icon located right after it.
Is there an easier way to connect in the same room?
If you are using iOS you can share your browser url using AirDrop. That way your friend would load the same room/game id by default.
Redux has proven to be very easy to debug and fix issues. You have one single entity that represents the state of your application and the only way you can change it is by dispatching actions. I was also surprised how fast it was to integrate with a node.js server and make it broadcast certain actions to the front-end.
I’ve learned a lot of cool things working on this project and I hope you find this post useful. Check out Tic-Tac-Toe.js on github and have fun! :)
Update: I've spoken about this topic @ React Amsterdam meetup. Check out the video below. Slides are also available on Speaker Deck.