Redux: Writing a Counter Reducer with Tests

Dan Abramov
InstructorDan Abramov

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 9 months ago

You will learn how to write the reducer for the counter application in a test driven development way, as well as the conventions in implementing reducers.

[00:00] The first function we're going to write is the reducer for the counter example. Reducer accepts straight and action as arguments and returns the next trait. Before jumping into the implementation, we're going to make certain assertions using Michael Jackson's Expect library. We're going to assert that when the straight of the counter is zero and you pass an increment action it should return one. Similarly it should return two when this straight is one and you increment.

[00:33] We're going to add a couple of tests that test how decrement works, which is that it decrements from two to one and from one to zero and we're going to add a log to tell if our tests are successful.

[00:48] If you ran this test, they're actually going to fail because we haven't even begun to implement our reducer. We're going to start by checking the action type and if the action type is increment we're going to return straight plus one, but if it is decrement we're going to return straight minus one.

[01:10] If you run the tests we will find that this is enough to get them to pass. However, there are still some flaws in our current implementation of the counter reducer. For example, I think that if we dispatch an action that it does not understand, it should return the current straight of the application.

[01:31] However, if we check for that we will see that this test fails, because we currently don't handle unknown actions. I'm going to add an else clause that returns the current straight. The tests pass now.

[01:46] Another issue is that while the reducer is normally in control of the application straight, currently it does not specify the initial straight. In the case of counter example that would be zero. The convention we use in Redux is that if the reducer receives undefined as the straight argument, it must return what it considers to be the initial straight of the application. In this case it will be zero.

[02:14] Now come a few cosmetic tweaks. I'll replace this bunch of tweaks with a switch statement and I'm going to replace this condition with ES6 default argument, which looks better. I'm also going to replace the function declaration with an arrow function, which has clearer semantics in ES6.

Nathaniel
Nathaniel
~ 6 years ago

Would you mind explaining more as to why you define counter as a const and an arrow function? What is meant by it has clearer semantics? Thanks so much, really enjoying the series so far!

Dan Abramov
Dan Abramovinstructor
~ 6 years ago

Nathaniel,

For historical reasons JS function declarations have weird semantics. "function" is not block-scoped (that it, behaves like "var" rather than like ES6 "let" or "const"), it is hoisted (a very weird thing to cloud beginners' heads with), it has "this" context which is frustrating to explain when you're just teaching some library's API, etc.

See also:

https://medium.com/@ryanflorence/functions-without-function-bc356ed34a2f

https://twitter.com/sebmarkbage/status/638531311477522433

rphansen91
rphansen91
~ 6 years ago

Which library are you using for expect?

rphansen91
rphansen91
~ 6 years ago

Figured out through later episodes it is loaded through as a script tag on the HTML src="https://wzrd.in/standalone/expect@latest". Loving the series so far, same architecture as ELM but with JS!

Ferenc
Ferenc
~ 5 years ago

If anyone wondering:

https://github.com/mjackson/expect

Janis
Janis
~ 2 years ago

Is expect a thing from expect.js that I need to npm install? Also what environment should I use for this mini tutorial series?