Skip to content

Commit 090f7b6

Browse files
committed
update tutorial, update step 6
1 parent 3d894df commit 090f7b6

File tree

9 files changed

+108
-80
lines changed

9 files changed

+108
-80
lines changed

coderoad.json

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@
259259
"05/03"
260260
],
261261
"hints": [
262+
"Try this: `case VOTE_UP: const pokemon = state.pokemon.map(...)`",
262263
"return `Object.assign({}, state, { pokemon: nextPokemon });`"
263264
]
264265
}
@@ -270,7 +271,7 @@
270271
"description": "Create modular, composable reducers with `combineReducers`.\n\nExplanation here.",
271272
"tasks": [
272273
{
273-
"description": "create a new `const reducers` and set it equal to \"reducer\". Pass \"reducers\" into your store for now, instead of \"reducer\". We'll use combineReducers shortly, but let's not break the app yet.",
274+
"description": "create a new `const reducers` and set it equal to \"reducer\". Pass \"reducers\" into your store for now, instead of \"reducer\". If this seems convoluted it is because we're trying not to break the app.",
274275
"tests": [
275276
"06/01"
276277
],
@@ -283,7 +284,7 @@
283284
]
284285
},
285286
{
286-
"description": "We're going to create more than one reducer. They can't all be called \"reducer\", so rename your original reducer \"pokemon\". Make sure to set reducers equal to the new name as well.",
287+
"description": "We're going to create more than one reducer. They can't all be called \"reducer\", so rename your original reducer \"pokemon\". Make sure to set `reducers` equal to the new name as well.",
287288
"tests": [
288289
"06/02"
289290
],
@@ -295,48 +296,61 @@
295296
]
296297
},
297298
{
298-
"description": "import `combineReducers` from redux",
299+
"description": "When we use `combineReducers` we will be able to define the initial state inside of each reducer. To keep things working, we'll have to do this step by step. Copy the \"pokemon\" key from `initialState` and give it its own variable called \"defaultPokemon\". It should be an array with three pokemon.",
299300
"tests": [
300301
"06/03"
301302
],
302303
"hints": [
303-
"Try this: `import { combineReducers } from 'redux';`"
304+
"Like this: `const defaultPokemon = [{ ... }, { ... }, { ... }]`"
304305
]
305306
},
306307
{
307-
"description": "combineReducers(), and pass in your reducer ({ pokemon })",
308+
"description": "Set your pokemon reducer first param, state, to equal \"pokemon\", and give it a default value of `defaultPokemon` using ES6 default params.",
308309
"tests": [
309310
"06/04"
310311
],
311312
"hints": [
312-
"Try this: `const reducers = combineReducers({pokemon});`"
313+
"Default params work like this: `fn(param1 = defaultParam, param2)`",
314+
"Like this: `const pokemon = (pokemon = defaultPokemon, action) => {`"
313315
]
314316
},
315317
{
316-
"description": "We're going to shake things up now to make our reducers more composable. Set the initial state inside of your `createStore` to simply be an empty object (`{}`)",
318+
"description": "Fix references to \"state\" inside your reducer to match the passed in value \"pokemon\"",
317319
"tests": [
318320
"06/05"
321+
],
322+
"hints": [
323+
"Change three references to \"pokemon\" in your pokemon reducer",
324+
"First: 'const pokemon = (pokemon = defaultPokemon, action) => {`",
325+
"Second: `const nextPokemon = pokemon.map(...)`",
326+
"Third: `default: return pokemon;`"
319327
]
320328
},
321329
{
322-
"description": "Thanks to `combineReducers` we can now define the initial state inside of each reducer. Get rid of \"initialState\", but keep the \"pokemon\" key and call it \"defaultPokemon\". It should be an array with three pokemon. Finally, pass the `defaultPokemon` as the default state in the pokemon reducer. You can use ES6 default params.",
330+
"description": "Our initial state is now handled by `defaultPokemon`. Set the initial state inside of your `createStore` to simply be an empty object (`{}`).",
323331
"tests": [
324332
"06/06"
325333
],
326334
"hints": [
327-
"Like this: `const defaultPokemon = [{ id: 1, name: 'Luvdisc', ...`",
328-
"Default params work like this: `fn(param1 = defaultParam, param2)`",
329-
"Like this: `const pokemon = (state = defaultPokemon, action) => {`"
335+
"Try this: `const store = createStore(reducers, {});`"
330336
]
331337
},
332338
{
333-
"description": "We no longer pass the entire \"state\" inside of our reducers, only the slice of our state the reducer needs to know. Rename all references to \"state\" inside of your \"pokemon\" reducer to what it really is now: \"pokemon\".",
339+
"description": "import `combineReducers` from redux",
334340
"tests": [
335341
"06/07"
336342
],
337343
"hints": [
338-
"Change three references to \"pokemon\" in your pokemon reducer",
339-
"Third: `default: return pokemon;`"
344+
"Try this: `import { combineReducers } from 'redux';`"
345+
]
346+
},
347+
{
348+
"description": "`reducers` should now call `combineReducers` instead and call `pokemon`. `combineReducers` takes an object with keys of each reducer.",
349+
"tests": [
350+
"06/08"
351+
],
352+
"hints": [
353+
"Try this: `const reducers = combineReducers({pokemon});`"
340354
]
341355
}
342356
],

tutorial/05/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const nextPokemon = state.pokemon.map(p => {
2929

3030
+ What if we were dealing with multiple keys on the state. We'd have to ensure that our changes return a complete new state each time. Use `Object.assign`
3131
@test('05/03')
32+
@hint('Try this: `case VOTE_UP: const pokemon = state.pokemon.map(...)`')
3233
@hint('return `Object.assign({}, state, { pokemon: nextPokemon });`')
3334

3435
@onPageComplete('Now that you have an idea of how reducers work. Next we can look at how to create multiple, modular reducers.')

tutorial/06/03.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
describe('03 combineReducers', () => {
1+
describe('03 defaultPokemon', () => {
22

3-
const combineReducers = indexJs.__get__('combineReducers');
3+
it('doesn\'t exist', () => {
4+
expect(defaultPokemon).to.not.be.undefined;
5+
});
6+
7+
it('should be a list of three pokemon', () => {
8+
expect(defaultPokemon).to.have.length(3);
9+
});
410

5-
it('should be loaded', () => {
6-
expect(combineReducers).to.not.be.undefined;
11+
it('should have the full pokemon data from before', () => {
12+
expect(defaultPokemon[0].name).to.equal('Luvdisc');
713
});
814

15+
// it('should be the default param for state in the pokemon reducer', () => {
16+
// expect(pokemon(undefined, { type: 'ANY' })).to.deep.equal(defaultPokemon);
17+
// });
18+
919
});

tutorial/06/04.js

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
1-
describe('04 reducers', () => {
1+
describe('04 pokemon reducer first param "state"', () => {
22

3-
it('should call "combineReducers"', () => {
4-
// combineReducers calls a function "combination"
5-
const regex = /function combination/;
6-
const string = reducers.toString();
7-
expect(string).to.match(regex);
8-
});
9-
10-
it('should call "combineReducers" with { pokemon }', () => {
11-
// combineReducers creates a new state with key values
12-
// matching the passed in reducers
13-
const keys = Object.keys(store.getState());
14-
expect(keys.includes('pokemon')).to.be.true;
3+
it('should have a default value of `defaultPokemon`', () => {
4+
155
});
166

177
});

tutorial/06/05.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
describe('05 stores initial state', () => {
1+
describe('05 "pokemon" reducer', () => {
22

3-
it('should be declared', () => {
4-
// the state will become greater after we declare our pokemon,
5-
// but for now let's make sure the state is just an empty object
6-
if (process.env.TASK_POSITION === '4') {
7-
expect(store.getState()).to.deep.equal({});
8-
}
3+
it('should reference "pokemon", not "state"', () => {
4+
const regex = /state/;
5+
const string = pokemon.toString();
6+
expect(string).to.not.match(regex);
7+
});
8+
9+
it('should still work on VOTE_UP', () => {
10+
const first = defaultPokemon[0];
11+
const target = first.votes + 1;
12+
const next = pokemon(undefined, { type: 'VOTE_UP', payload: { id: first.id } });
13+
expect(next[0].votes).to.equal(target);
914
});
1015

1116
});

tutorial/06/06.js

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
1-
describe('06 defaultPokemon', () => {
2-
3-
it('doesn\'t exist', () => {
4-
expect(defaultPokemon).to.not.be.undefined;
5-
});
6-
7-
it('should be a list of three pokemon', () => {
8-
expect(defaultPokemon).to.have.length(3);
9-
});
10-
11-
it('should have the full pokemon data from before', () => {
12-
expect(defaultPokemon[0].name).to.equal('Luvdisc');
13-
});
14-
15-
it('should be the default param for state in the pokemon reducer', () => {
16-
expect(pokemon(undefined, { type: 'ANY' })).to.deep.equal(defaultPokemon);
1+
describe('06 stores initial state', () => {
2+
3+
it('should be declared', () => {
4+
// the state will become greater after we declare our pokemon,
5+
// but for now let's make sure the state is just an empty object
6+
if (process.env.TASK_POSITION === '4') {
7+
expect(store.getState()).to.deep.equal({});
8+
}
179
});
1810

1911
});

tutorial/06/07.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
describe('07 "pokemon" reducer', () => {
1+
describe('07 combineReducers', () => {
22

3-
it('should reference "pokemon", not "state"', () => {
4-
const regex = /state/;
5-
const string = pokemon.toString();
6-
expect(string).to.not.match(regex);
7-
});
3+
const combineReducers = indexJs.__get__('combineReducers');
84

9-
it('voteUp should still work', () => {
10-
const first = defaultPokemon[0];
11-
const target = first.votes + 1
12-
const next = pokemon(undefined, { type: 'VOTE_UP', payload: { id: first.id } });
13-
expect(next.pokemon[0].votes).to.equal(target);
5+
it('should be loaded', () => {
6+
expect(combineReducers).to.not.be.undefined;
147
});
158

169
});

tutorial/06/08.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
describe('08 reducers', () => {
2+
3+
it('should call "combineReducers"', () => {
4+
// combineReducers calls a function "combination"
5+
const regex = /function combination/;
6+
const string = reducers.toString();
7+
expect(string).to.match(regex);
8+
});
9+
10+
it('should call "combineReducers" with { pokemon }', () => {
11+
// combineReducers creates a new state with key values
12+
// matching the passed in reducers
13+
const keys = Object.keys(store.getState());
14+
expect(keys.includes('pokemon')).to.be.true;
15+
});
16+
17+
});

tutorial/06/index.md

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,46 @@ Create modular, composable reducers with `combineReducers`.
33

44
Explanation here.
55

6-
+ create a new `const reducers` and set it equal to "reducer". Pass "reducers" into your store for now, instead of "reducer". We'll use combineReducers shortly, but let's not break the app yet.
6+
+ create a new `const reducers` and set it equal to "reducer". Pass "reducers" into your store for now, instead of "reducer". If this seems convoluted it is because we're trying not to break the app.
77
@test('06/01')
88
@action(open('src/index.js'))
99
@hint('First, try this: `const reducers = reducer;`')
1010
@hint('Second, try this: `const store = createStore(reducers, initialState);`')
1111

12-
+ We're going to create more than one reducer. They can't all be called "reducer", so rename your original reducer "pokemon". Make sure to set reducers equal to the new name as well.
12+
+ We're going to create more than one reducer. They can't all be called "reducer", so rename your original reducer "pokemon". Make sure to set `reducers` equal to the new name as well.
1313
@test('06/02')
1414
@hint('First, rename "pokemon" to "reducer"')
1515
@hint('Like this: `const pokemon = (state, action) => {...}`')
1616
@hint('Second, change your "reducers" to equal "pokemon"')
1717
@hint('Like this: `const reducers = pokemon;`')
1818

19-
+ import `combineReducers` from redux
19+
+ When we use `combineReducers` we will be able to define the initial state inside of each reducer. To keep things working, we'll have to do this step by step. Copy the "pokemon" key from `initialState` and give it its own variable called "defaultPokemon". It should be an array with three pokemon.
2020
@test('06/03')
21-
@hint('Try this: `import { combineReducers } from 'redux';`')
21+
@hint('Like this: `const defaultPokemon = [{ ... }, { ... }, { ... }]`')
2222

23-
+ combineReducers(), and pass in your reducer ({ pokemon })
23+
+ Set your pokemon reducer first param, state, to equal "pokemon", and give it a default value of `defaultPokemon` using ES6 default params.
2424
@test('06/04')
25-
@hint('Try this: `const reducers = combineReducers({pokemon});`')
25+
@hint('Default params work like this: `fn(param1 = defaultParam, param2)`')
26+
@hint('Like this: `const pokemon = (pokemon = defaultPokemon, action) => {`')
2627

27-
+ We're going to shake things up now to make our reducers more composable. Set the initial state inside of your `createStore` to simply be an empty object (`{}`)
28+
+ Fix references to "state" inside your reducer to match the passed in value "pokemon"
2829
@test('06/05')
30+
@hint('Change three references to "pokemon" in your pokemon reducer')
31+
@hint('First: 'const pokemon = (pokemon = defaultPokemon, action) => {`')
32+
@hint('Second: `const nextPokemon = pokemon.map(...)`')
33+
@hint('Third: `default: return pokemon;`')
2934

30-
+ Thanks to `combineReducers` we can now define the initial state inside of each reducer. Get rid of "initialState", but keep the "pokemon" key and call it "defaultPokemon". It should be an array with three pokemon. Finally, pass the `defaultPokemon` as the default state in the pokemon reducer. You can use ES6 default params.
35+
+ Our initial state is now handled by `defaultPokemon`. Set the initial state inside of your `createStore` to simply be an empty object (`{}`).
3136
@test('06/06')
32-
@hint('Like this: `const defaultPokemon = [{ id: 1, name: 'Luvdisc', ...`')
33-
@hint('Default params work like this: `fn(param1 = defaultParam, param2)`')
34-
@hint('Like this: `const pokemon = (state = defaultPokemon, action) => {`')
35-
37+
@hint('Try this: `const store = createStore(reducers, {});`')
3638

37-
+ We no longer pass the entire "state" inside of our reducers, only the slice of our state the reducer needs to know. Rename all references to "state" inside of your "pokemon" reducer to what it really is now: "pokemon".
39+
+ import `combineReducers` from redux
3840
@test('06/07')
39-
@hint('Change three references to "pokemon" in your pokemon reducer')
40-
@hint('Third: `default: return pokemon;`')
41+
@hint('Try this: `import { combineReducers } from 'redux';`')
42+
43+
+ `reducers` should now call `combineReducers` instead and call `pokemon`. `combineReducers` takes an object with keys of each reducer.
44+
@test('06/08')
45+
@hint('Try this: `const reducers = combineReducers({pokemon});`')
46+
4147

4248
@onPageComplete('The state remains the same, but now our reducers are much more modular. In the next step, we will separate our code into it's own file')

0 commit comments

Comments
 (0)