You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -21,9 +21,9 @@ CodeRoad is an open-sourced interactive tutorial platform for the Atom Editor. L
21
21
22
22
##### Project Setup
23
23
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.
25
25
26
-
---
26
+
We will be making a "Worst Pokemon" voting app. For the app, we'll need to setup some build tools.
27
27
28
28
Running `> npm run setup` will do the following:
29
29
@@ -34,91 +34,235 @@ Running `> npm run setup` will do the following:
34
34
35
35
You'll find this "setup" script located in your *package.json*.
36
36
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).
37
38
38
-
---
39
+
##### The Store
39
40
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.
41
42
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.
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
+
59
73
##### 2. Action Creators
60
74
75
+
An **action creator** is a functions that creates an action.
76
+
61
77
```js
62
78
constactionName= () => ({ type:'ACTION_NAME' });
63
79
```
64
80
81
+
Action creators make it easy to pass params into an action.
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
+
67
91
```js
68
-
constACTION_NAME='ACTION_NAME'
92
+
constACTION_NAME='ACTION_NAME';
93
+
constGET_ITEM='GET_ITEM';
94
+
95
+
constaction= () => ({ type:ACTION_NAME });
96
+
constgetItem= (id) => ({ type:GET_ITEM, payload: { id }});
Redux totes itself as a "predictable" state container. This predictability is the product of some helpful restrictions that force us to write better code.
85
141
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.
87
147
88
-
Notes
89
148
```js
90
-
constnextPokemon=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
+
returnstate.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.
100
168
101
169
##### Combine Reducers
102
170
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
+
```
104
190
105
-
Explanation here.
191
+
As our app grows, we can now think of the data in smaller chunks.
Let's try refactoring our app to use `combineReducers`.
106
196
107
197
##### File Structure
108
198
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
110
227
111
-
Explanation here
228
+
For simplicity in this example, we'll try putting our files together by function.
112
229
113
230
##### Logger
114
231
115
-
The power of middleware with "redux-logger".
232
+
We still haven't touched on one of the most powerful features of Redux: **middleware**.
116
233
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
+
conststore=>next=>action=> {
247
+
// do something magical here
248
+
returnnext(action);
249
+
// returns result of reducer called with action
250
+
}
251
+
```
252
+
253
+
Let's try out the power of middleware with "redux-logger".
118
254
119
255
##### Second Action
120
256
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:
122
266
123
267
```js
124
268
functionsortByVotes(a, b) {
@@ -130,8 +274,25 @@ function sortByVotes(a, b) {
130
274
}
131
275
```
132
276
133
-
Sort pokemon by votes
277
+
Let's setup a `SORT_BY_POPULARITY` action to be called after each vote.
134
278
135
279
##### Thunk
136
280
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
+
conststore=>next=>action=> {
287
+
if (typeof action ==='function') {
288
+
// it's a thunk!
289
+
}
290
+
returnnext(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