Skip to content

Commit a3289a6

Browse files
committed
add explanation to all steps
1 parent c5a1e23 commit a3289a6

19 files changed

+537
-213
lines changed

README.md

Lines changed: 196 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ CodeRoad is an open-sourced interactive tutorial platform for the Atom Editor. L
2121

2222
##### Project Setup
2323

24-
Getting a project setup is rarely easy. Luckily, we have a quick script that can do the work for us.
24+
Welcome! In this tutorial series we'll be exploring Redux, a tool for predictably managing state in your app.
2525

26-
---
26+
We will be making a "Worst Pokemon" voting app. For the app, we'll need to setup some build tools.
2727

2828
Running `> npm run setup` will do the following:
2929

@@ -34,91 +34,235 @@ Running `> npm run setup` will do the following:
3434

3535
You'll find this "setup" script located in your *package.json*.
3636

37+
> We'll be installing several NPM packages from terminal. You may consider installing a plugin for adding a terminal inside your editor, such as ["platformio-ide-terminal"](https://github.com/platformio/platformio-atom-ide-terminal).
3738
38-
---
39+
##### The Store
3940

40-
We'll be installing a lot of scripts from terminal. You may also want to consider installing the atom package ["platformio-ide-terminal"](https://github.com/platformio/platformio-atom-ide-terminal), which provides a terminal inside your editor.
41+
In Redux, the **store** is the boss. Think of the **store** as holding the "single source of truth" of your application data.
4142

42-
##### The Store
43+
Once created, the **store** has several helpful methods:
44+
- `getState` to read the current data of your app.
45+
- `dispatch` to trigger actions. We'll look at actions more later.
46+
- `subscribe` to listen for state changes
4347

44-
The "single source of truth".
48+
> [Learn more](http://redux.js.org/docs/basics/Store.html).
4549
46-
```js
47-
const store = createStore(reducer, initialState);
48-
```
50+
Let's get started by settings up the **store** for your Redux app.
4951

5052
##### Actions
5153

52-
Events that change the data.
54+
An **action** is a named event that can trigger a change in your application data.
55+
56+
Actions are often broken into three parts to make your code more readable.
5357

5458
##### 1. Actions
59+
60+
An **action** includes a named "type".
5561
```js
5662
const action = { type: 'ACTION_NAME' };
5763
```
5864

65+
Actions may also include other possible params needed to transform that data.
66+
67+
```js
68+
const getItem = { type: 'GET_ITEM', clientId: 42, payload: { id: 12 } };
69+
```
70+
71+
Normal params passed in are often put inside of a `payload` object. This is part of a standard called [Flux Standard Action](https://github.com/acdlite/flux-standard-action). Other common fields include `error` & `meta`.
72+
5973
##### 2. Action Creators
6074

75+
An **action creator** is a functions that creates an action.
76+
6177
```js
6278
const actionName = () => ({ type: 'ACTION_NAME' });
6379
```
6480

81+
Action creators make it easy to pass params into an action.
82+
83+
```js
84+
const getItem = (clientId, id) => ({ type: 'GET_ITEM', clientId: 42, payload: { id: 12 } });
85+
```
86+
6587
##### 3. Action Types
6688

89+
Often, the action name is also extracted as an **action type**. This is helpful for readability and to catch action name typos. Additionally, most editors will auto-complete your action types from the variable name.
90+
6791
```js
68-
const ACTION_NAME = 'ACTION_NAME'
92+
const ACTION_NAME = 'ACTION_NAME';
93+
const GET_ITEM = 'GET_ITEM';
94+
95+
const action = () => ({ type: ACTION_NAME });
96+
const getItem = (id) => ({ type: GET_ITEM, payload: { id }});
6997
```
7098

99+
> [Learn more](http://redux.js.org/docs/basics/Actions.html).
100+
101+
Let's write an action for voting up your choice of worst pokemon.
102+
71103
##### Reducer
72104

73-
The data transformation
105+
A **reducer** is what handles the actual data transformation triggered by an action.
106+
107+
In it's simplest form, a **reducer** is just a function with the current **state** and current **action** passed in.
74108

75109
```js
76-
const reducer = (state) => {
110+
const reducer = (state, action) => {
77111
console.log(state);
78112
return state;
79113
};
80114
```
81115

116+
We can handle different actions by matching on the action type. If no matches are found, we just return the original state.
117+
118+
```js
119+
const ACTION_NAME = 'ACTION_NAME';
120+
121+
const reducer = (state, action) => {
122+
switch(action.type) {
123+
// match on action.type === ACTION_NAME
124+
case ACTION_NAME:
125+
state = 42;
126+
// return new state after transformation
127+
return state;
128+
default:
129+
return state;
130+
}
131+
};
132+
```
133+
134+
Our reducer is passed in as the first param when we create our **store**.
135+
136+
> [Learn more](http://redux.js.org/docs/basics/Reducers.html).
137+
82138
##### Pure Functions
83139

84-
Reducers must be pure functions
140+
Redux totes itself as a "predictable" state container. This predictability is the product of some helpful restrictions that force us to write better code.
85141

86-
State is "read only".
142+
One such guideline: reducers must be pure functions.
143+
144+
##### Mutation
145+
146+
When an action passes through a reducer, it should not "mutate" the data, but rather return a new state altogether.
87147

88-
Notes
89148
```js
90-
const nextPokemon = state.pokemon.map(p => {
91-
if (id === p.id) {
92-
p.votes += 1;
93-
}
94-
return p;
95-
});
96-
return {
97-
pokemon: nextPokemon
98-
};
99-
```
149+
case ADD_TO_ARRAY:
150+
/* bad */
151+
state.push(42); // push mutates the state
152+
return state;
153+
```
154+
155+
If multiple actions were pushing into the state, the functions are no longer **pure** and thus no longer fully predictable.
156+
157+
##### Pure
158+
159+
By returning an entirely new array, we can be sure that our state will be **pure** and thus predictable.
160+
161+
```js
162+
case ADD_TO_ARRAY:
163+
/* good */
164+
return state.concat(42); // returns a new array, with 42 on the end
165+
```
166+
167+
Let's give writing pure reducers a try as we implement our `VOTE_UP` action.
100168

101169
##### Combine Reducers
102170

103-
Create modular, composable reducers with `combineReducers`.
171+
In Redux, we are not limited to writing a long, single reducer. Using `combineReducers` allows us to create modular, composable reducers.
172+
173+
As our state is an object, for example:
174+
175+
```js
176+
{
177+
pokemon: [ ... ],
178+
users: [ ... ]
179+
}
180+
```
181+
182+
We can create a reducer to handle data transformations for each key in our state.
183+
184+
```js
185+
{
186+
pokemon: pokemonReducer,
187+
users: usersReducer
188+
}
189+
```
104190

105-
Explanation here.
191+
As our app grows, we can now think of the data in smaller chunks.
192+
193+
> [Learn more](http://redux.js.org/docs/api/combineReducers.html).
194+
195+
Let's try refactoring our app to use `combineReducers`.
106196

107197
##### File Structure
108198

109-
Refactor your project into different files.
199+
Our "index.js" file is getting a little long. Of course, our app will be more maintainable if we can divide it across different, well structured files.
200+
201+
There are different ways of structuring your app:
202+
203+
##### 1. Files By Type
204+
205+
- store.js
206+
- action-types.js
207+
- action-creators.js
208+
- reducers.js
209+
210+
##### 2. Files By Function
211+
212+
- store.js
213+
- reducers.js
214+
- modules
215+
- pokemon
216+
- index.js
217+
218+
##### 3. Files by Function & Type
219+
220+
- store
221+
- reducers.js
222+
- modules
223+
- pokemon
224+
- actions.js
225+
- reducer.js
226+
- action-types.js
110227

111-
Explanation here
228+
For simplicity in this example, we'll try putting our files together by function.
112229

113230
##### Logger
114231

115-
The power of middleware with "redux-logger".
232+
We still haven't touched on one of the most powerful features of Redux: **middleware**.
116233

117-
Explanation here.
234+
Middleware is triggered on each action.
235+
236+
```
237+
1. Dispatch(action)
238+
-> 2. Middleware(state, action)
239+
-> 3. Reducer(state, action)
240+
-> 4. state
241+
```
242+
243+
Middleware is created with the `store`. In it's most basic form, middleware can look like the function below:
244+
245+
```js
246+
const store => next => action => {
247+
// do something magical here
248+
return next(action);
249+
// returns result of reducer called with action
250+
}
251+
```
252+
253+
Let's try out the power of middleware with "redux-logger".
118254

119255
##### Second Action
120256

121-
Creating a "SORT_BY_POPULARITY" action.
257+
Notice how the votes remain out of order. Let's create a sorting action for putting the highest votes at the top.
258+
259+
For this, we'll use a sorting function. A sorting function takes two values, and returns either:
260+
261+
- `1`: move ahead
262+
- `-1`: move behind
263+
- `0`: no change
264+
265+
See an example for sorting votes below:
122266

123267
```js
124268
function sortByVotes(a, b) {
@@ -130,8 +274,25 @@ function sortByVotes(a, b) {
130274
}
131275
```
132276

133-
Sort pokemon by votes
277+
Let's setup a `SORT_BY_POPULARITY` action to be called after each vote.
134278

135279
##### Thunk
136280

137-
Using thunks for async actions.
281+
As we've seen in the previous steps, thunks sound more complicated than they really are. A thunk is just a function that returns a function.
282+
283+
Inside of middleware, we can determine if an action is returning a function.
284+
285+
```js
286+
const store => next => action => {
287+
if (typeof action === 'function') {
288+
// it's a thunk!
289+
}
290+
return next(action);
291+
}
292+
```
293+
294+
If it is a thunk, we can pass in two helpful params:
295+
- `store.dispatch`
296+
- `store.getState`
297+
298+
As we'll see, `dispatch` alone can allow us to create async or multiple actions.

0 commit comments

Comments
 (0)