This site runs best with JavaScript enabled.

Make Impossible States Impossible

September 10, 2018

Video Blogger

Photo by tom coe on Unsplash


A simple trick to simplify your application and component state

This is a phrase I first heard from David Khourshid in his talk at React Rally 2017 Infinitely Better UIs with Finite Automata: "Make impossible states impossible" (super great talk by the way, and xstate is awesome, and David is too). Googling around it looks like it's a pretty popular phrase in the Elm community, though I'm not sure who said it first.

To illustrate what this means, let's checkout at a very simple example:

1<Alert>Just FYI</Alert>
2<Alert success>It worked!</Alert>
3<Alert warning>Head's up</Alert>
4<Alert danger>Watch out!</Alert>

Rendered example

You may have used or written a component that has this kind of API. It's nice and clean (in case you're unfamiliar, in JSX, a non-assigned prop like that is the same as assigning it to the value "true", so success is the same as success={true}).

Here's where this kind of API falls over. What should I render with this?

1<Alert success warning>
2 It worked!
3</Alert>

Should it blend the colors? Should I choose one? Which do I choose? Or should we just yell at the developer trying to do this because they obviously don't know what they're doing? (tip: that last one is NOT what you should do).

The idea of making impossible states impossible basically means that situations and questions like these should never come up. It means that you design APIs that make a clear distinction between the possible states of a component. This makes the component easier to maintain and to use.

So what do we do with our simple example? Whelp, all these different props represent is the type of alert that should be rendered. So what if instead of simply accepting the prop itself, we accept a type prop?

1<AlertBetter>Just FYI</AlertBetter>
2<AlertBetter type="success">It worked!</AlertBetter>
3<AlertBetter type="warning">Head's up</AlertBetter>
4<AlertBetter type="danger">Watch out!</AlertBetter>

Now it's impossible to have more states than one because there are only three valid values for the type prop (well, four if you count undefined)! It's easier to maintain, easier to explain/understand, and harder to mess up. Everyone wins!

Conclusion

There are various ways to do this effectively and converting a boolean value to an enum is only one such mechanism. It can and does get more complicated, but the concept can seriously simplify your component's and application's state. This is why I recommend you give David's talk a watch (here it is again). And give xstate a solid look as well. There's some good ideas in that! Good luck!

P.S. After publishing this, several people noted that this phrase was the title of a talk from Richard Feldman at Elm Conf. There's also a talk by Patrick Stapfer from ReasonML Munich Meetup. You may also be interested to check out a similar blog post by @stereobooster.

P.S.P.S. Further revelation about the origin of a similar phrase:

Learn more about React from me:

Things to not miss:

  • Babel 7 Released — Congratulations to the Babel team for releasing Babel 7! I wrote the portion on babel-plugin-macros!
  • Simply React: In my ReactRally talk ⚛️I tell a familiar story about an component that you can probably relate to. Then I explain how it could have gone better. I think you'll enjoy this!
  • fastpack - a JavaScript bundler (like webpack, but with fewer features at the moment) that is BLAZING FAST 🔥🔥🔥🔥 (bundles 1000 modules in < 1 second!)
  • Byteconf React — Byteconf React is a 100% free conference with the best JavaScript and React speakers in the world. Conferences are great, but flights, hotels, and tickets are expensive, so not everyone can go. Byteconf is streamed on Twitch, for free, so anyone and everyone can attend. We're building a community of developers around the world — see you there?

Bonus:

Here's a part from my section in the babel 7 blog post that didn't make the final cut but I thought you'd enjoy:

Here's a very simple example of a custom macro that swaps lineand column imports with the line or column where it appears in the code:

1// line-column.macro
2module.exports = createMacro(lineColumnMacro)
3function lineColumnMacro({references, babel}) {
4 references.line.forEach(referencePath => {
5 const num = referencePath.node.loc.start.line
6 referencePath.replaceWith(babel.types.numberLiteral(num))
7 })
8 references.column.forEach(referencePath => {
9 const num = referencePath.node.loc.start.column
10 referencePath.replaceWith(babel.types.numberLiteral(num))
11 })
12}

And then this code:

1`import {line, column} from './line-column.macro'
2`
3``console.log(`we're at ${line}:${column}`)
4console.log(`and now we're at ${line}:${column}`)``

This will be transpiled into:

1console.log(`we're at ${3}:${32}`)
2console.log(`and now we're at ${4}:${40}`)

And we don't need to change any config to add that custom transform. Writing and using code transforms is a fair amount easier this way. You can't do everything that a full babel plugin can do, but we're only just getting started with this and look forward to what the community will do with this power.

Play around with the above macro example > on astexplorer.net. See if you can make it support an import called _lineColumn_that is replaced with the sum of line and column. Just for fun!

Discuss on TwitterEdit post on GitHub

Share article
loading relevant upcoming workshops...
Kent C. Dodds

Kent C. Dodds is a JavaScript software engineer and teacher. He's taught hundreds of thousands of people how to make the world a better place with quality software development tools and practices. He lives with his wife and four kids in Utah.