Make Impossible States Impossible
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:
<alert>Just FYI</alert> <alert success="">It worked!</alert> <alert warning="">Head's up</alert> <alert danger="">Watch out!</alert>
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?
<alert success="" warning="">It worked!</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?
<alertbetter>Just FYI</alertbetter> <alertbetter type="success">It worked!</alertbetter> <alertbetter type="warning">Head's up</alertbetter> <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!
Looking for a job? Looking for a developer? Check out my job board: kcd.im/jobs
Learn more about React from me:
- The Beginner’s Guide to React
- Advanced React Component Patterns (also on Frontend Masters).
- Confidently Ship Production React Apps - A 30 talk on Egghead (30 minutes… weird right?!) about testing React components.
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 line
and column
imports with the line or column where it appears in the code:
// line-column.macro module.exports = createMacro(lineColumnMacro) function lineColumnMacro({ references, babel }) { references.line.forEach(referencePath => { const num = referencePath.node.loc.start.line referencePath.replaceWith(babel.types.numberLiteral(num)) }) references.column.forEach(referencePath => { const num = referencePath.node.loc.start.column referencePath.replaceWith(babel.types.numberLiteral(num)) }) }
import {line, column} from './line-column.macro' console.log(`we're at ${line}:${column}`) console.log(`and now we're at ${line}:${column}`)
This will be transpiled into:
console.log(`we're at ${3}:${32}`) console.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!
Some tweets from this last week:
> What’s your favorite way to automate setting up a new laptop? β 21 Aug 2018 (some interesting tips here!)
> This is great advice and is one of the biggest reasons I dislike shallow rendering. (quoted “Design your tests so it’s easy to change how you build but hard to break what you build.”) β 26 Aug 2018
> Well this tells an interesting story about my GitHub activity over the years… β 25 Aug 2018
This week’s blog post is “Why and How I started public speaking”. It’s the published version of my newsletter from 2 weeks ago. If you thought it was good, go ahead and give it some claps (πx50) and a retweet:
Special thanks to my sponsor Patreons: Hashnode
P.S. If you like this, make sure to subscribe, follow me on twitter, buy me lunch, support me on patreon, and share this with your friends π
π Hi! Iβm Kent C. Dodds. I work at PayPal as a full stack JavaScript engineer. I represent PayPal on the TC39. Iβm actively involved in the open source community. Iβm an instructor on egghead.io, Frontend Masters, and Workshop.me. Iβm also a Google Developer Expert. Iβm happily married and the father of four kids. I like my family, code, JavaScript, and React.