Skip to content

Commit 5748456

Browse files
committed
init sync up
0 parents  commit 5748456

28 files changed

+1653
-0
lines changed

Diff for: .babelrc

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"stage": 0,
3+
/* avoid config inheritance:
4+
https://github.com/happypoulp/redux-tutorial/issues/88
5+
http://stackoverflow.com/questions/32540598/disable-babelrc-inheritance
6+
*/
7+
"breakConfig": true
8+
}

Diff for: .gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
npm-debug.log
3+
.DS_Store
4+
dist
5+
nested-dispatch.js
6+
test.js
7+
.idea

Diff for: 00_introduction.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Tutorial 0 - introduction.js
2+
3+
// Why this tutorial?
4+
// While trying to learn Redux, I realized that I had accumulated incorrect knowledge about flux through
5+
// articles I read and personal experience. I don't mean that articles about flux are not well written
6+
// but I just didn't grasp concepts correctly. In the end, I was just applying documentation of different
7+
// flux frameworks (Reflux, Flummox, FB Flux) and trying to make them match with the theoretical concept I read
8+
// about (actions / actions creators, store, dispatcher, etc).
9+
// Only when I started using Redux did I realize that flux is more simple than I thought. This is all
10+
// thanks to Redux being very well designed and having removed a lot of "anti-boilerplate features" introduced
11+
// by other frameworks I tried before. I now feel that Redux is a much better way to learn about flux
12+
// than many other frameworks. That's why I want now to share with everyone, using my own words,
13+
// flux concepts that I am starting to grasp, focusing on the use of Redux.
14+
15+
// You may have seen this diagram representing the famous unidirectional data flow of a flux application:
16+
17+
/*
18+
_________ ____________ ___________
19+
| | | | | |
20+
| Action |------------▶| Dispatcher |------------▶| callbacks |
21+
|_________| |____________| |___________|
22+
▲ |
23+
| |
24+
| |
25+
_________ ____|_____ ____▼____
26+
| |◀----| Action | | |
27+
| Web API | | Creators | | Store |
28+
|_________|----▶|__________| |_________|
29+
▲ |
30+
| |
31+
____|________ ____________ ____▼____
32+
| User | | React | | Change |
33+
| interactions |◀--------| Views |◀-------------| events |
34+
|______________| |___________| |_________|
35+
36+
*/
37+
38+
// In this tutorial we'll gradually introduce you to concepts of the diagram above. But instead of trying
39+
// to explain this complete diagram and the overall flow it describes, we'll take each piece separately and try to
40+
// understand why it exists and what role it plays. In the end you'll see that this diagram makes perfect sense
41+
// once we understand each of its parts.
42+
43+
// But before we start, let's talk a little bit about why flux exists and why we need it...
44+
// Let's pretend we're building a web application. What are all web applications made of?
45+
// 1) Templates / html = View
46+
// 2) Data that will populate our views = Models
47+
// 3) Logic to retrieve data, glue all views together and to react accordingly to user events,
48+
// data modifications, etc. = Controller
49+
50+
// This is the very classic MVC that we all know about. But it actually looks like concepts of flux,
51+
// just expressed in a slightly different way:
52+
// - Models look like stores
53+
// - user events, data modifications and their handlers look like
54+
// "action creators" -> action -> dispatcher -> callback
55+
// - Views look like React views (or anything else as far as flux is concerned)
56+
57+
// So is flux just a matter of new vocabulary? Not exactly. But vocabulary DOES matter, because by introducing
58+
// these new terms we are now able to express more precisely things that were regrouped under
59+
// various terminologies... For example, isn't a data fetch an action? Just like a click is also an action?
60+
// And a change in an input is an action too... Then we're all already used to issuing actions from our
61+
// applications, we were just calling them differently. And instead of having handlers for those
62+
// actions directly modify Models or Views, flux ensures all actions go first through something called
63+
// a dispatcher, then through our stores, and finally all watchers of stores are notified.
64+
65+
// To get more clarity how MVC and flux differ, we'll
66+
// take a classic use-case in an MVC application:
67+
// In a classic MVC application you could easily end up with:
68+
// 1) User clicks on button "A"
69+
// 2) A click handler on button "A" triggers a change on Model "A"
70+
// 3) A change handler on Model "A" triggers a change on Model "B"
71+
// 4) A change handler on Model "B" triggers a change on View "B" that re-renders itself
72+
73+
// Finding the source of a bug in such an environment when something goes wrong can become quite challenging
74+
// very quickly. This is because every View can watch every Model, and every Model can watch other Models, so
75+
// basically data can arrive from a lot of places and be changed by a lot of sources (any views or any models).
76+
77+
// Whereas when using flux and its unidirectional data flow, the example above could become:
78+
// 1) user clicks on button "A"
79+
// 2) a handler on button "A" triggers an action that is dispatched and produces a change on Store "A"
80+
// 3) since all other stores are also notified about the action, Store B can react to the same action too
81+
// 4) View "B" gets notified by the change in Stores A and B, and re-renders
82+
83+
// See how we avoid directly linking Store A to Store B? Each store can only be
84+
// modified by an action and nothing else. And once all stores have replied to an action,
85+
// views can finally update. So in the end, data always flows in one way:
86+
// action -> store -> view -> action -> store -> view -> action -> ...
87+
88+
// Just as we started our use case above from an action, let's start our tutorial with
89+
// actions and action creators.
90+
91+
// Go to next tutorial: 01_simple-action-creator.js

Diff for: 01_simple-action-creator.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Tutorial 1 - simple-action-creator.js
2+
3+
// We started to talk a little about actions in the introduction but what exactly are those "action creators"
4+
// and how are they linked to "actions"?
5+
6+
// It's actually so simple that a few lines of code can explain it all!
7+
8+
// The action creator is just a function...
9+
var actionCreator = function() {
10+
// ...that creates an action (yeah, the name action creator is pretty obvious now) and returns it
11+
return {
12+
type: 'AN_ACTION'
13+
}
14+
}
15+
16+
// So is that all? yes.
17+
18+
// However, one thing to note is the format of the action. This is kind of a convention in flux
19+
// that the action is an object that contains a "type" property. This type allows for further
20+
// handling of the action. Of course, the action can also contain other properties to
21+
// pass any data you want.
22+
23+
// We'll also see later that the action creator can actually return something other than an action,
24+
// like a function. This will be extremely useful for async action handling (more on that
25+
// in dispatch-async-action.js).
26+
27+
// We can call this action creator and get an action as expected:
28+
console.log(actionCreator())
29+
// Output: { type: 'AN_ACTION' }
30+
31+
// Ok, this works but it does not go anywhere...
32+
// What we need is to have this action be sent somewhere so that
33+
// anyone interested could know that something happened and could act accordingly.
34+
// We call this process "Dispatching an action".
35+
36+
// To dispatch an action we need... a dispatch function ("Captain obvious").
37+
// And to let anyone interested know that an action happened, we need a mechanism to register
38+
// "handlers". Such "handlers" to actions in traditional flux application are called stores and
39+
// we'll see in the next section how they are called in redux.
40+
41+
// So far here is the flow of our application:
42+
// ActionCreator -> Action
43+
44+
// Read more about actions and action creators here:
45+
// http://rackt.org/redux/docs/recipes/ReducingBoilerplate.html
46+
47+
// Go to next tutorial: 02_about-state-and-meet-redux.js

Diff for: 02_about-state-and-meet-redux.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Tutorial 02 - about-state-and-meet-redux.js
2+
3+
// Sometimes the actions that we'll handle in our application will not only inform us
4+
// that something happened but also tell us that data needs to be updated.
5+
6+
// This is actually quite a big challenge in any app.
7+
// Where do I keep all the data regarding my application along its lifetime?
8+
// How do I handle modification of such data?
9+
// How do I propagate modifications to all parts of my application?
10+
11+
// Here comes Redux.
12+
13+
// Redux (https://github.com/rackt/redux) is a "predictable state container for JavaScript apps"
14+
15+
// Let's review the above questions and reply to them with
16+
// Redux vocabulary (flux vocabulary too for some of them):
17+
18+
// Where do I keep all the data regarding my application along its lifetime?
19+
// You keep it the way you want (JS object, array, Immutable structure, ...).
20+
// Data of your application will be called state. This makes sense since we're talking about
21+
// all the application's data that will evolve over time, it's really the application's state.
22+
// But you hand it over to Redux (Redux is a "state container", remember?).
23+
// How do I handle data modifications?
24+
// Using reducers (called "stores" in traditional flux).
25+
// A reducer is a subscriber to actions.
26+
// A reducer is just a function that receives the current state of your application, the action,
27+
// and returns a new state modified (or reduced as they call it)
28+
// How do I propagate modifications to all parts of my application?
29+
// Using subscribers to state's modifications.
30+
31+
// Redux ties all this together for you.
32+
// To sum up, Redux will provide you:
33+
// 1) a place to put your application state
34+
// 2) a mechanism to dispatch actions to modifiers of your application state, AKA reducers
35+
// 3) a mechanism to subscribe to state updates
36+
37+
// The Redux instance is called a store and can be created like this:
38+
/*
39+
import { createStore } from 'redux'
40+
var store = createStore()
41+
*/
42+
43+
// But if you run the code above, you'll notice that it throws an error:
44+
// Error: Invariant Violation: Expected the reducer to be a function.
45+
46+
// That's because createStore expects a function that will allow it to reduce your state.
47+
48+
// Let's try again
49+
50+
import { createStore } from 'redux'
51+
52+
var store = createStore(() => {})
53+
54+
// Seems good for now...
55+
56+
// Go to next tutorial: 03_simple-reducer.js

Diff for: 03_simple-reducer.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Tutorial 03 - simple-reducer.js
2+
3+
// Now that we know how to create a Redux instance that will hold the state of our application
4+
// we will focus on those reducer functions that will allow us to transform this state.
5+
6+
// A word about reducer VS store:
7+
// As you may have noticed, in the flux diagram shown in the introduction, we had "Store", not
8+
// "Reducer" like Redux is expecting. So how exactly do Store and Reducer differ?
9+
// It's more simple than you could imagine: A Store keeps your data in it while a Reducer doesn't.
10+
// So in traditional flux, stores hold state in them while in Redux, each time a reducer is
11+
// called, it is passed the state that needs to be updated. This way, Redux's stores became
12+
// "stateless stores" and were renamed reducers.
13+
14+
// As stated before, when creating a Redux instance you must give it a reducer function...
15+
16+
import { createStore } from 'redux'
17+
18+
var store_0 = createStore(() => {})
19+
20+
// ... so that Redux can call this function on your application state each time an action occurs.
21+
// Giving reducer(s) to createStore is exactly how redux registers the action "handlers" (read reducers) we
22+
// were talking about in section 01_simple-action-creator.js.
23+
24+
// Let's put some log in our reducer
25+
26+
var reducer = function (...args) {
27+
console.log('Reducer was called with args', args)
28+
}
29+
30+
var store_1 = createStore(reducer)
31+
32+
// Output: Reducer was called with args [ undefined, { type: '@@redux/INIT' } ]
33+
34+
// Did you see that? Our reducer is actually called even if we didn't dispatch any action...
35+
// That's because to initialize the state of the application,
36+
// Redux actually dispatches an init action ({ type: '@@redux/INIT' })
37+
38+
// When called, a reducer is given those parameters: (state, action)
39+
// It's then very logical that at an application initialization, the state, not being
40+
// initialized yet, is "undefined"
41+
42+
// But then what is the state of our application after Redux sends its "init" action?
43+
44+
// Go to next tutorial: 04_get-state.js

Diff for: 04_get-state.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Tutorial 04 - get-state.js
2+
3+
// How do we retrieve the state from our Redux instance?
4+
5+
import { createStore } from 'redux'
6+
7+
var reducer_0 = function (state, action) {
8+
console.log('reducer_0 was called with state', state, 'and action', action)
9+
}
10+
11+
var store_0 = createStore(reducer_0)
12+
// Output: reducer_0 was called with state undefined and action { type: '@@redux/INIT' }
13+
14+
// To get the state that Redux is holding for us, you call getState
15+
16+
console.log('store_0 state after initialization:', store_0.getState())
17+
// Output: store_0 state after initialization: undefined
18+
19+
// So the state of our application is still undefined after the initialization? Well of course it is,
20+
// our reducer is not doing anything... Remember how we described the expected behavior of a reducer in
21+
// "about-state-and-meet-redux"?
22+
// "A reducer is just a function that receives the current state of your application, the action,
23+
// and returns a new state modified (or reduced as they call it)"
24+
// Our reducer is not returning anything right now so the state of our application is what
25+
// reducer() returns, hence "undefined".
26+
27+
// Let's try to send an initial state of our application if the state given to reducer is undefined:
28+
29+
var reducer_1 = function (state, action) {
30+
console.log('reducer_1 was called with state', state, 'and action', action)
31+
if (typeof state === 'undefined') {
32+
return {}
33+
}
34+
35+
return state;
36+
}
37+
38+
var store_1 = createStore(reducer_1)
39+
// Output: reducer_1 was called with state undefined and action { type: '@@redux/INIT' }
40+
41+
console.log('store_1 state after initialization:', store_1.getState())
42+
// Output: store_1 state after initialization: {}
43+
44+
// As expected, the state returned by Redux after initialization is now {}
45+
46+
// There is however a much cleaner way to implement this pattern thanks to ES6:
47+
48+
var reducer_2 = function (state = {}, action) {
49+
console.log('reducer_2 was called with state', state, 'and action', action)
50+
51+
return state;
52+
}
53+
54+
var store_2 = createStore(reducer_2)
55+
// Output: reducer_2 was called with state {} and action { type: '@@redux/INIT' }
56+
57+
console.log('store_2 state after initialization:', store_2.getState())
58+
// Output: store_2 state after initialization: {}
59+
60+
// You've probably noticed that since we've used the default parameter on state parameter of reducer_2,
61+
// we no longer get undefined as state's value in our reducer's body.
62+
63+
// Let's now recall that a reducer is only called in response to an action dispatched and
64+
// let's fake a state modification in response to an action type 'SAY_SOMETHING'
65+
66+
var reducer_3 = function (state = {}, action) {
67+
console.log('reducer_3 was called with state', state, 'and action', action)
68+
69+
switch (action.type) {
70+
case 'SAY_SOMETHING':
71+
return {
72+
...state,
73+
message: action.value
74+
}
75+
default:
76+
return state;
77+
}
78+
}
79+
80+
var store_3 = createStore(reducer_3)
81+
// Output: reducer_3 was called with state {} and action { type: '@@redux/INIT' }
82+
83+
console.log('store_3 state after initialization:', store_3.getState())
84+
// Output: redux state after initialization: {}
85+
86+
// Nothing new in our state so far since we did not dispatch any action yet. But there are few
87+
// important things to pay attention to in the last example:
88+
// 0) I assumed that our action contains a type and a value property. The type property is mostly
89+
// a convention in flux actions and the value property could have been anything else.
90+
// 1) You'll often see the pattern involving a switch to respond appropriately
91+
// to an action received in your reducers
92+
// 2) When using a switch, NEVER forget to have a "default: return state" because
93+
// if you don't, you'll end up having your reducer return undefined (and lose your state).
94+
// 3) Notice how we returned a new state made by merging current state with { message: action.value },
95+
// all that thanks to this awesome ES7 notation (Object Spread): { ...state, message: action.value }
96+
// 4) Note also that this ES7 Object Spread notation suits our example because it's doing a shallow
97+
// copy of { message: action.value } over our state (meaning that first level properties of state
98+
// are completely overwritten - as opposed to gracefully merged - by first level property of
99+
// { message: action.value }). But if we had a more complex / nested data structure, you might choose
100+
// to handle your state's updates very differently:
101+
// - using Immutable.js (https://facebook.github.io/immutable-js/)
102+
// - using Object.assign (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
103+
// - using manual merge
104+
// - or whatever other strategy that suits your needs and the structure of your state since
105+
// Redux is absolutely NOT opinionated on this (remember, Redux is a state container).
106+
107+
// Now that we're starting to handle actions in our reducer let's talk about having multiple reducers and
108+
// combining them.
109+
110+
// Go to next tutorial: 05_combine-reducers.js

0 commit comments

Comments
 (0)