Created
July 22, 2019 01:50
-
-
Save jgierer12/e4e92eefddd14f1ace4f671f4507365d to your computer and use it in GitHub Desktop.
This file has been truncated, but you can view the full file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"> | |
<channel> | |
<title><![CDATA[Kent C. Dodds]]></title> | |
<description><![CDATA[Come check out how Kent C. Dodds can help you level up your career as a software engineer.]]></description> | |
<link>https://kentcdodds.com</link> | |
<generator>RSS for Node</generator> | |
<lastBuildDate>Fri, 19 Jul 2019 20:25:23 GMT</lastBuildDate> | |
<item> | |
<title><![CDATA[When to break up a component into multiple components]]></title> | |
<description><![CDATA[Current Available Translations: Korean Did you know that you could write any React Application as a single React | |
Component? There's absolutely nothing technically stopping React from putting | |
your entire application into one giant component. Your…]]></description> | |
<link>https://kentcdodds.com/blog/when-to-break-up-a-component-into-multiple-components</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/when-to-break-up-a-component-into-multiple-components</guid> | |
<pubDate>Fri, 19 Jul 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Current Available Translations:</p><ul><li><a href="https://edykim.com/ko/post/when-to-break-up-a-component-into-multiple-components">Korean</a></li></ul><p>Did you know that you could write any React Application as a single React | |
Component? There&#x27;s absolutely nothing technically stopping React from putting | |
your entire application into one giant component. Your function would be HUGE. | |
There&#x27;d be a TON of hooks for state and side-effects, but it&#x27;s totally possible.</p><p>If you tried this though you&#x27;d face a few problems:</p><ol><li>Performance would probably suffer: Every state change results in a re-render | |
of the entire application.</li><li>Code sharing/reusability would be... not easy. At least if you made it a | |
class component, which you might have to do if you wanted to use | |
<code>componentDidCatch</code> to handle runtime errors. If you were allowed to use | |
<code>react-error-boundary</code> so you could use hooks, then it would be | |
<em>considerably</em> easier. But... I digress...</li><li>State would be a challenge: Knowing which pieces of state and event handlers | |
went with what parts of JSX would make your head hurt 😬 and lead to some | |
hard to track down bugs 🐜 (This is one benefit of the separation of | |
concerns).</li><li>Testing would be 100% integration: | |
<a href="https://kentcdodds.com/blog/write-tests">Not necessarily an altogether bad thing</a>, but it would | |
be pretty tough to test edge cases and keep things isolated to the parts | |
that you&#x27;re trying to test, so you would suffer big-time from over-testing | |
(which is <a href="https://kentcdodds.com/blog/common-testing-mistakes">a common mistake in E2E testing</a>).</li><li>Working together on the codebase with multiple engineers would just be | |
terrible. Can you imagine the git diffs and merge conflicts?!</li><li>Using third party component libraries would be... ummm... impossible? If | |
we&#x27;re writing everything as a single component third party libraries is at | |
odds with that goal! And even if we allowed using third party components, | |
what about HOCs like <a href="https://emotion.sh">react-emotion</a>? Not allowed! | |
(Besides, you should <a href="https://emotion.sh/docs/css-prop">use the <code>css</code> prop</a> | |
anyway 😉).</li><li>Encapsulating imperative abstractions/APIs in a more declarative component | |
API wouldn&#x27;t be allowed either meaning that the imperative API would be | |
littered throughout the lifecycle hooks of our one giant component, leading | |
to harder to follow code. (Again, unless you&#x27;re using hooks, in which case | |
you could group relevant hooks together, making it easier to manage, but | |
still a bit of a nightmare).</li></ol><p>These are the reasons that we write custom components. It allows us to solve | |
these problems.</p><p>I&#x27;ve had a question on my AMA for a while: | |
<a href="https://github.com/kentcdodds/ama/issues/399">Best ways/patterns to split app into components</a>. | |
And this is my answer: &quot;When you experience one of the problems above, that&#x27;s | |
when you break your component into multiple smaller components. NOT BEFORE.&quot; | |
Breaking a single component into multiple components is what&#x27;s called | |
&quot;abstraction.&quot; Abstraction is awesome, but | |
<a href="https://kentcdodds.com/blog/aha-programming">every abstraction comes with a cost</a>, and you have to be | |
aware of that cost and the benefits before you take the plunge</p><blockquote><p><em><a href="https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction">&quot;Duplication is far cheaper than the wrong abstraction.&quot;</a> | |
— <a href="https://twitter.com/sandimetz">Sandi Metz</a>.</em></p></blockquote><p>So I don&#x27;t mind if the JSX I return in my component function gets really long. | |
Remember that JSX is just a bunch of JavaScript expressions using the | |
declarative APIs given by components. Not a whole lot can go wrong with code | |
like that and it&#x27;s much easier to keep that code as it is than breaking out | |
things into a bunch of smaller components and start | |
<a href="https://kentcdodds.com/blog/prop-drilling">Prop Drilling</a> everywhere.</p><h3>Conclusion</h3><p>So feel free to break up your components into smaller ones, but don&#x27;t be afraid | |
of a growing component until you start experiencing real problems. It&#x27;s WAY | |
easier to maintain it until it needs to be broken up than maintain a pre-mature | |
abstraction. Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/when-to-break-up-a-component-into-multiple-components">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Write tests. Not too many. Mostly integration.]]></title> | |
<description><![CDATA[Current Available Translations: Korean Russian Portuguese I've given this blog post as a talk which you can watch here: https://youtu.be/Fha2bVoC8SE?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf A while back, Guillermo Rauch (creator of | |
Socket.io and…]]></description> | |
<link>https://kentcdodds.com/blog/write-tests</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/write-tests</guid> | |
<pubDate>Sat, 13 Jul 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Current Available Translations:</p><ul><li><a href="https://www.vobour.com/%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%98%EC%9E%90-%EB%84%88%EB%AC%B4-%EB%A7%8E%EC%9D%B4%EB%8A%94-%EB%A7%90%EA%B3%A0-%ED%86%B5%ED%95%A9-%EC%9C%84%EC%A3%BC%EB%A1%9C-write-tests">Korean</a></li><li><a href="http://howtorecover.me/napisite-testy-ne-sliskom-mnogo">Russian</a></li><li><a href="https://medium.com/@sergioamjr91/escreva-testes-n%C3%A3o-muitos-mas-mais-de-integra%C3%A7%C3%A3o-7ebebf225516">Portuguese</a></li></ul><p>I&#x27;ve given this blog post as a talk which you can watch here:</p><p><iframe width="100%" height="315" src="https://www.youtube-nocookie.com/embed/Fha2bVoC8SE?rel=0&amp;list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></p><p>A while back, <a href="https://twitter.com/rauchg">Guillermo Rauch</a> (creator of | |
<a href="https://socket.io">Socket.io</a> and founder of <a href="https://zeit.co">Zeit.co</a> (the | |
company behind a ton of the awesome stuff coming out lately)) | |
<a href="https://twitter.com/rauchg/status/807626710350839808">tweeted something profound</a>:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Write tests. Not too many. Mostly integration.</p>— Guillermo ▲ (@rauchg) <a href="https://twitter.com/rauchg/status/807626710350839808">December 10, 2016</a></blockquote></p><blockquote><p><strong><em>Write tests. Not too many. Mostly integration.</em></strong></p></blockquote><p>This is deep, albeit short, so let&#x27;s dive in:</p><h2>Write tests.</h2><p>Yes, for most projects you should write automated tests. You should if you value | |
your time anyway. Much better to catch a bug locally from the tests than getting | |
a call at 2:00 in the morning and fix it then. <strong>Often I find myself saving time | |
when I put time in to write tests.</strong> It may or may not take longer to implement | |
what I&#x27;m building, but I (and others) will almost definitely save time | |
maintaining it.</p><p>The thing you should be thinking about when writing tests is how much confidence | |
they bring you that your project is free of bugs. Static typing and linting | |
tools like <a href="https://www.typescriptlang.org">TypeScript</a> and | |
<a href="https://eslint.org">ESLint</a> can get you a remarkable amount of confidence, and | |
if you&#x27;re not using these tools I highly suggest you give them a look. That | |
said, <strong>even a strongly typed language should have tests.</strong> Typing and linting | |
can&#x27;t ensure your business logic is free of bugs. So you can still seriously | |
increase your confidence with a good test suite.</p><h2>Not too many.</h2><p>I&#x27;ve heard managers and teams mandating 100% code coverage for applications. | |
That&#x27;s a really bad idea. The problem is that <strong>you get diminishing returns on | |
your tests as the coverage increases much beyond 70%</strong> (I made that number up... | |
no science there). Why is that? Well, when you strive for 100% all the time, you | |
find yourself spending time testing things that really don&#x27;t need to be tested. | |
Things that really have no logic in them at all (so any bugs could be caught by | |
ESLint and Flow). <em>Maintaining tests like this actually really slow you and your | |
team down.</em></p><p>You may also find yourself testing implementation details just so you can make | |
sure you get that one line of code that&#x27;s hard to reproduce in a test | |
environment. You <em>really</em> want to avoid testing implementation details because | |
it doesn&#x27;t give you very much confidence that your application is working and it | |
slows you down when refactoring. <strong>You should very rarely have to change tests | |
when you refactor code.</strong></p><p>I should mention that almost all of my open source projects have 100% code | |
coverage. This is because most of my open source projects are smaller libraries | |
and tools that are reusable in many different situations (a breakage could lead | |
to a serious problem in a lot of consuming projects) and they&#x27;re relatively easy | |
to get 100% code coverage on anyway.</p><h2>Mostly integration.</h2><p>There are all sorts of different types of testing (check out my 5 minute talk | |
about it at Fluent Conf: | |
<a href="https://youtu.be/Da9wfQ0frGA?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">&quot;What we can learn about testing from the wheel&quot;</a>). | |
They each have trade-offs. The three most common forms of testing we&#x27;re talking | |
about when we talk of automated testing are: Unit, Integration, and End to End.</p><p>Here&#x27;s <a href="http://slides.com/kentcdodds/testing-workshop#/4/8">a slide</a> from my | |
Frontend Masters workshop: | |
<a href="https://frontendmasters.com/courses/testing-javascript">&quot;Testing JavaScript Applications&quot;</a>.</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/20c422f5dad15169acfae9613c561dad/8ff1e/2.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:62.5%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="testing pyramid" title="testing pyramid" src="https://kentcdodds.com/static/20c422f5dad15169acfae9613c561dad/8ff1e/2.png" srcSet="https://kentcdodds.com/static/20c422f5dad15169acfae9613c561dad/f4a45/2.png 259w,https://kentcdodds.com/static/20c422f5dad15169acfae9613c561dad/ef0f6/2.png 518w,https://kentcdodds.com/static/20c422f5dad15169acfae9613c561dad/8ff1e/2.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>This testing pyramid is a combination of one I got from | |
<a href="https://martinfowler.com/bliki/TestPyramid.html">Martin Fowler&#x27;s blog</a> and one | |
I got from | |
<a href="https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html">the Google Testing blog</a>.</p><p>As indicated here, the pyramid shows from bottom to top: Unit, Integration, E2E. | |
As you move up the pyramid the tests get slower to write/run and more expensive | |
(in terms of time and resources) to run/maintain. It&#x27;s meant to indicate that | |
you should spend more of your time on unit tests due to these factors.</p><p>One thing that it doesn&#x27;t show though is that <strong>as you move up the pyramid, the | |
confidence quotient of each form of testing increases.</strong> You get more bang for | |
your buck. So while E2E tests may be slower and more expensive than unit tests, | |
they bring you much more confidence that your application is working as | |
intended.</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">The TestPyramid blog post by <a href="https://twitter.com/martinfowler">@martinfowler</a> (<a href="https://t.co/RCv8abFm37">https://t.co/RCv8abFm37</a>) finishes with this note.<br/><br/>Our tools are awesome now and this assumption is less true. This is why I say goodby to the pyramid 🔼 and hello to the trophy 🏆 <a href="https://t.co/EKTK4vqj6n">https://t.co/EKTK4vqj6n</a> <a href="https://t.co/Wa8brUiWfy">pic.twitter.com/Wa8brUiWfy</a></p>— Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1141365123296051201">June 19, 2019</a></blockquote></p><p>As noted, our tools have moved beyond the assumption in Martin&#x27;s original | |
Testing Pyramid concept. This is why I created &quot;The Testing Trophy&quot; 🏆</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">&quot;The Testing Trophy&quot; 🏆<br/><br/>A general guide for the **return on investment** 🤑 of the different forms of testing with regards to testing JavaScript applications.<br/><br/>- End to end w/ <a href="https://twitter.com/Cypress_io">@Cypress_io</a> ⚫️<br/>- Integration &amp; Unit w/ <a href="https://twitter.com/fbjest">@fbjest</a> 🃏<br/>- Static w/ <a href="https://twitter.com/flowtype">@flowtype</a> 𝙁 and <a href="https://twitter.com/geteslint">@geteslint</a> ⬣ <a href="https://t.co/kPBC6yVxSA">pic.twitter.com/kPBC6yVxSA</a></p>— Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/960723172591992832">February 6, 2018</a></blockquote></p><hr/><p>Here&#x27;s a humorous other illustration of the importance of integration tests:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">expect(umbrellaOpens).toBe(true)<br/><br/>tests: 1 passed, 1 total<br/><br/>**all tests passed** <a href="https://t.co/p6IKO7KDuy">pic.twitter.com/p6IKO7KDuy</a></p>— Erin 🦁 (@erinfranmc) <a href="https://twitter.com/erinfranmc/status/1148986961207730176">July 10, 2019</a></blockquote></p><p>It doesn&#x27;t matter if your component <code>&lt;A /&gt;</code> renders component <code>&lt;B /&gt;</code> with props | |
<code>c</code> and <code>d</code> if component <code>&lt;B /&gt;</code> actually breaks if prop <code>e</code> is not supplied. So | |
while having some unit tests to verify these pieces work in isolation isn&#x27;t a | |
bad thing, <em>it doesn&#x27;t do you any good if you don&#x27;t <strong>also</strong> verify that they | |
work together properly.</em> And you&#x27;ll find that by testing that they work together | |
properly, you often don&#x27;t need to bother testing them in isolation.</p><p><strong>Integration tests strike a great balance on the trade-offs between confidence | |
and speed/expense.</strong> This is why it&#x27;s advisable to spend <em>most</em> (not all, mind | |
you) of your effort there.</p><blockquote><p>For more on this read | |
<a href="https://kentcdodds.com/blog/testing-implementation-details">Testing Implementation Details</a>. For | |
more about the different distinctions of tests, read | |
<a href="https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests">Static vs Unit vs Integration vs E2E Testing for Frontend Apps</a></p></blockquote><h2>How to write more integration tests</h2><p>The line between integration and unit tests is a little bit fuzzy. Regardless, I | |
think the biggest thing you can do to write more integration tests is to <strong>stop | |
mocking so much stuff</strong>. <em>When you mock something you’re removing all confidence | |
in the integration between what you’re testing and what’s being mocked.</em> I | |
understand that <a href="https://kentcdodds.com/blog/the-merits-of-mocking">sometimes it can’t be helped</a> | |
(though <a href="https://youtu.be/EaxDl5NPuCA">some would disagree</a>). You don’t | |
<em>actually</em> want to send emails or charge credit cards every test, but most of | |
the time you can avoid mocking and you’ll be better for it.</p><p><strong>If you’re doing React, then this includes shallow rendering.</strong> For more on | |
this, read | |
<a href="https://kentcdodds.com/blog/why-i-never-use-shallow-rendering">Why I Never Use Shallow Rendering</a>.</p><h2>Conclusion</h2><p>I don&#x27;t think that anyone can argue that testing software is a waste of time. | |
The biggest challenge is <a href="https://kentcdodds.com/blog/how-to-know-what-to-test">knowing what to test</a> | |
and how to test it in a way that gives | |
<a href="https://kentcdodds.com/blog/confidently-shipping-code">true confidence</a> rather than the false | |
confidence of | |
<a href="https://kentcdodds.com/blog/testing-implementation-details">testing implementation details</a>.</p><p>I hope this is helpful to you and I wish you the best luck in your goals to find | |
confidence in shipping your applications!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/write-tests">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React Fundamentals: Props vs State]]></title> | |
<description><![CDATA[Let's compare props and state. Here's a definition of each: "props" (short for "properties") is an object of arbitrary inputs a React | |
function component accepts as the first argument. "state" is data that changes over the lifetime of a specific…]]></description> | |
<link>https://kentcdodds.com/blog/props-vs-state</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/props-vs-state</guid> | |
<pubDate>Mon, 08 Jul 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Let&#x27;s compare props and state. Here&#x27;s a definition of each:</p><ul><li>&quot;props&quot; (short for &quot;properties&quot;) is an object of arbitrary inputs a React | |
function component accepts as the first argument.</li><li>&quot;state&quot; is data that changes over the lifetime of a specific instance of a | |
React component.</li></ul><p>Let&#x27;s dive into each.</p><h2>Props</h2><p>Think of props as arguments to a function. React components are functions which | |
return JSX (or more generally something that&#x27;s renderable like React elements, | |
<code>null</code>, a string, etc.). Typically when you have a piece of code that you would | |
like to reuse, you can place that code into a function and any dynamic values | |
that code used before can be accepted as arguments (for example | |
<code>const five = 2 + 3</code> could be extracted to a function and accept arguments like | |
so <code>const five = add(2, 3)</code>).</p><p>The same is true of a piece of JSX, except instead of calling it like a normal | |
function (<code>add(2, 3)</code>) you use JSX syntax (<code>&lt;Add n1={2} n2={3} /&gt;</code>). The | |
&quot;attributes&quot; supplied in the JSX are what are called <code>props</code> and they are placed | |
together in a single object and passed to the <code>Add</code> component function as the | |
first argument like so:</p><pre><code class="language-jsx" metastring="{1}">function Add(props) { | |
return ( | |
&lt;div&gt; | |
{props.n1} + {props.n2} = {props.n1 + props.n2} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>If I were to use this like so:</p><pre><code class="language-jsx">&lt;Add n1={2} n2={3} /&gt; | |
</code></pre><p>Here&#x27;s how that would be rendered:</p><div style="padding:14px;background-color:rgba(0,0,0,0.05);border-radius:4px;margin-bottom:20px"><div>2 + 3 = 5</div></div><blockquote><p>NOTE: Props can be anything. In our example they&#x27;re numbers, but they can also | |
be (and often are) strings, arrays, objects, functions, etc.</p></blockquote><p>Let&#x27;s say we want to default the <code>n2</code> to <code>0</code> in the event someone doesn&#x27;t | |
provide it (like <code>&lt;Add n1={2} /&gt;</code>). <strong>One limit to props is that you&#x27;re not | |
allowed to change them.</strong> So you couldn&#x27;t do something like this:</p><pre><code class="language-jsx">function Add(props) { | |
if (typeof props.n2 === &#x27;undefined&#x27;) { | |
props.n2 = 0 | |
} | |
return ( | |
&lt;div&gt; | |
{props.n1} + {props.n2} = {props.n1 + props.n2} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>If we try to do this, we&#x27;ll get the following error:</p><pre><code>TypeError: Cannot add property n2, object is not extensible | |
</code></pre><p>This is simple enough to solve though:</p><pre><code class="language-jsx">function Add(props) { | |
let n2 = props.n2 | |
if (typeof n2 === &#x27;undefined&#x27;) { | |
n2 = 0 | |
} | |
return ( | |
&lt;div&gt; | |
{props.n1} + {n2} = {props.n1 + n2} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>Or, often, you&#x27;ll find people use destructuring syntax with default values as | |
well (this is my personal preference):</p><pre><code class="language-jsx">function Add({n1, n2 = 0}) { | |
return ( | |
&lt;div&gt; | |
{n1} + {n2} = {n1 + n2} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>This is awesome, but what if I want to dynamically change the props value? Let&#x27;s | |
say I wanted to build something like this:</p><div style="padding:14px;background-color:rgba(0,0,0,0.05);border-radius:4px;margin-bottom:20px"><div>2 + <input type="number" aria-label="n2" value="0"/> = 2</div></div><p>Without state, here&#x27;s how I might try to accomplish that:</p><pre><code class="language-jsx" metastring="{5}">function AddWithInput(props) { | |
function handleInputChange(event) { | |
const input = event.target | |
const newN2 = Number(input.value) | |
props.n2 = newN2 | |
} | |
return ( | |
&lt;div&gt; | |
{props.n1} +{&#x27; &#x27;} | |
&lt;input type=&quot;number&quot; value={props.n2} onChange={handleInputChange} /&gt; ={&#x27; &#x27;} | |
{props.n1 + props.n2} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>However, this will not work for two reasons:</p><ol><li>React doesn&#x27;t know that we&#x27;ve updated the <code>n2</code> value of our <code>props</code> object, | |
so it wont update the DOM when we change <code>props.n2</code>, so we wont see our | |
changes anyway</li><li>We&#x27;ll get the <code>TypeError</code> warning as before</li></ol><p>This is where state comes in.</p><h2>State</h2><p>State is data that changes over time, and that&#x27;s perfect for our situation:</p><pre><code class="language-jsx" metastring="{2, 7}">function AddWithInput(props) { | |
const [n2, setN2] = React.useState(0) | |
function handleInputChange(event) { | |
const input = event.target | |
const newN2 = Number(input.value) | |
setN2(newN2) | |
} | |
return ( | |
&lt;div&gt; | |
{props.n1} +{&#x27; &#x27;} | |
&lt;input type=&quot;number&quot; value={n2} onChange={handleInputChange} /&gt; ={&#x27; &#x27;} | |
{props.n1 + n2} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>That will work, and this is precisely what React state is intended to be used | |
for. It&#x27;s intended to track data values over the lifetime of the component (so | |
long as the component exists on the page).</p><p>However, users of the <code>AddWithInput</code> component can no longer set the initial | |
value of <code>n2</code> anymore. With the way that component is implemented currently, | |
it&#x27;s not referencing <code>props.n2</code> at all. But we can make this work by using props | |
when we initialize our state:</p><pre><code class="language-jsx">function AddWithInput(props) { | |
const [n2, setN2] = React.useState(props.n2) | |
// ... etc... | |
} | |
</code></pre><p>Now if someone were to do this: <code>&lt;AddWithInput n1={2} n2={3} /&gt;</code> then the result | |
would look like this (notice that the initial input value is <code>3</code>):</p><div style="padding:14px;background-color:rgba(0,0,0,0.05);border-radius:4px;margin-bottom:20px"><div>2 + <input type="number" aria-label="n2" value="3"/> = 5</div></div><p>So our props are &quot;arguments&quot; or &quot;inputs&quot; that we can pass to a component, and | |
state is something that is managed within the component and can change over | |
time.</p><p>Let me just clean this component up a little bit and I&#x27;ll explain my changes:</p><pre><code class="language-jsx" metastring="{1, 2}">function AddWithInput({n1, initialN2 = 0}) { | |
const [n2, setN2] = React.useState(initialN2) | |
function handleInputChange(event) { | |
const input = event.target | |
const newN2 = Number(input.value) | |
setN2(newN2) | |
} | |
return ( | |
&lt;div&gt; | |
{n1} + {&#x27; &#x27;} | |
&lt;input type=&quot;number&quot; value={n2} onChange={handleInputChange} /&gt; ={&#x27; &#x27;} | |
{n1 + n2} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>I changed to destructuring with defaults for the props, and I changed the prop | |
from <code>n2</code> to <code>initialN2</code>. When I&#x27;m using a prop value to initialize a state | |
value, I typically like to give it the prefix <code>initial</code> to communicate that | |
changes in that prop will not be taken into account. If that&#x27;s what you want, | |
then you&#x27;ll want to | |
<a href="https://reactjs.org/docs/lifting-state-up.html">Lift State Up</a>.</p><h2>Conclusion</h2><p>I hope this helps clear up the difference between props and state in React for | |
you. It&#x27;s a foundational concept. Go ahead and test yourself on this little app | |
below. Where&#x27;s the state, where are the props?</p><p><iframe src="https://codesandbox.io/embed/react-codesandbox-oov7o?view=editor" style="width:100%;height:500px;border-width:0px;border-radius:4px;overflow:hidden"></iframe></p><p>I hope that&#x27;s helpful! Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/props-vs-state">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How I Teach]]></title> | |
<description><![CDATA[I've been teaching programming for a long time. From the very beginning | |
actually. I signed up to be a tutor in my first programming class. I did it out | |
of an effort to | |
solidify what I was learning . Through the | |
years, I've changed the way that I…]]></description> | |
<link>https://kentcdodds.com/blog/how-i-teach</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-i-teach</guid> | |
<pubDate>Mon, 01 Jul 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I&#x27;ve been teaching programming for a long time. From the very beginning | |
actually. I signed up to be a tutor in my first programming class. I did it out | |
of an effort to | |
<a href="https://kentcdodds.com/blog/solidifying-what-you-learn">solidify what I was learning</a>. Through the | |
years, I&#x27;ve changed the way that I teach programming concepts to people.</p><p>In the last few years, I read a book that totally changed the way I think about | |
learning and teaching. When I was in school, I found myself constantly | |
&quot;cramming&quot; for tests. Spending hours before the test just re-reading the text | |
over and over again hoping it would stick. I couldn&#x27;t tell you anything about | |
the stuff I crammed, because I can&#x27;t remember it at all. I didn&#x27;t retain that | |
information.</p><p>In my teaching, I&#x27;m not interested in hooking people up to a firehose of | |
information and turning on the water. That&#x27;s easy. We all have tons of | |
information in our individual brains that we could just talk about for hours. | |
Doing that doesn&#x27;t do anyone any good. I&#x27;m more interested in ensuring that the | |
things I teach find a place in the students&#x27; brains and stay locked in forever. | |
So I&#x27;ve studied and experimented and one method has been the most effective for | |
me for the last few years and I want to share the concept with you now.</p><p>I read a book a few years ago about this. It&#x27;s called | |
<a href="https://makeitstick.net">Make It Stick: The Science of Successful Learning</a> and | |
it was a game changer for me. I recommend you pick it up and give it a | |
read/<a href="https://www.audible.com/pd/Make-It-Stick-Audiobook/B00M0EO7EY">listen</a>, | |
but here&#x27;s a great summary video about it:</p><p><iframe width="100%" height="315" src="https://www.youtube-nocookie.com/embed/tQsIlnuAB9E?rel=0" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></p><p>In that book, I learned that everything I knew about studying and learning was | |
totally wrong. And because of this I couldn&#x27;t teach as effectively. Learning how | |
people learn and master material made a big impact on the way that I approach | |
teaching it.</p><p>Here are a few of the things that I started doing after reading this book:</p><h3>Generation - Desirable Difficulties</h3><blockquote><p>Make and challenge assumptions about brand new concepts</p></blockquote><p>I have people <em>do</em> the exercises before I teach the concepts they&#x27;ll be | |
learning. I use &quot;emoji guides&quot; | |
(<a href="https://github.com/kentcdodds/react-fundamentals/blob/b230ea412aaaca50ecf2537c0cf72516cd0e68e1/src/exercises/06.js#L6-L18">like this</a>) | |
so they can actually get something accomplished during the exercise. Sometimes | |
the emoji guides do a ton of hand holding. But making attendees go through the | |
exercise first opens their brain to prepare it for learning.</p><p>This is often pretty uncomfortable for people. Especially for traditional | |
learners, we&#x27;re used to attending a lecture, listening to someone talk for an | |
hour, and writing down everything they&#x27;re saying. That may help you become good | |
at transcribing what someone&#x27;s saying very quickly, but it&#x27;s not going to help | |
you learn the material.</p><p>By struggling and facing challenges first, your brain has to work and you have | |
to make and challenge assumptions that you have about what it is you&#x27;re | |
learning. The beautiful thing about this is that once you&#x27;ve made a decision | |
based on your assumption you&#x27;re either correct or you&#x27;re not.</p><p>If your assumption was correct, then you get a shot of dopamine which will lodge | |
the knowledge into your brain better. You&#x27;re not going to get that same shot of | |
dopamine if someone just tells you the answer without you struggling to get | |
there.</p><p>If your assumption was incorrect, then you remember it even better because | |
there&#x27;s a bit of a disappointed feeling associated with getting it wrong and | |
<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5573739">that strong emotion will solidify that experience into your memory</a>. | |
(I should note that this is generally an internal dialog/experience that is | |
personal for each learner and their misconceptions are only made known to other | |
learners if they choose to share them during the instruction).</p><p>Having to struggle through the new concepts before being taught that material is | |
one of the ways I teach people and help them retain what they&#x27;re learning.</p><h3>Interleave Practice</h3><blockquote><p>Mix up what you&#x27;re learning/practicing as you&#x27;re learning new things</p></blockquote><p>In the book (and as described in the summary video), they reference a study | |
where baseball players practiced using different approaches. One group practiced | |
hitting the same pitch over and over again, then moved on to a different pitch | |
and practiced that over and over again. The other group had the pitches mixed | |
up. The one which mixed up the kinds of pitches they received performed better. | |
We can do that in the things that we&#x27;re learning and the way we teach as well.</p><p>This one is a bit harder to do in a single day workshop with specific things | |
that we want to learn, but in my material I try to get this by having the | |
exercises build upon one another so people see the new things they&#x27;ve been | |
learning on as they work on learning something new.</p><h3>Elaboration</h3><blockquote><p>Describe what you learned in your own words.</p></blockquote><p>At the end of <em>every</em> exercise, I have a link to an &quot;Elaboration and Feedback&quot; | |
form specific to the exercise. This is where I invite people to write down a few | |
of the things that they learned (in their own words). Doing this helps them to | |
connect what they&#x27;ve learned to things they already know which is another | |
important element for retention.</p><p>This one also benefits me because it&#x27;s an opportunity for me to receive some | |
quick feedback on the material so I can find places to improve things. I&#x27;ve been | |
doing this in my workshops since the beginning of 2017, and now over a year and | |
a half later I have 3.6k responses to this form. Not everyone fills out the | |
feedback and elaboration form, but I have been able to see which exercises and | |
working and which aren&#x27;t based on the responses I get here.</p><h3>Active Retrieval</h3><blockquote><p>Intentionally go back and make your brain try to remember what it&#x27;s naturally | |
forgetting.</p></blockquote><p>All of my workshop material is available as an open source project on GitHub | |
(&quot;available for private, non-commercial use under the GPL version 3&quot;). I | |
encourage everyone who takes the workshop material to go through it all again a | |
week later to interrupt the natural forgetting process that our brains do | |
automatically (think of it as natural garbage collection).</p><p>What&#x27;s best is if the workshop attendee can put what they&#x27;ve learned into | |
practice. However, because I can&#x27;t do much about deciding what they work on in | |
their day job, the next best thing I can do is make the material available for | |
them to go through again and encourage them to do so.</p><p>By practicing it again and actively retrieving the things your brain is in the | |
process of forgetting, you tell your brain &quot;nope, this is important, keep it | |
around please.&quot; The more you do that, the better you&#x27;ll remember it, which is | |
one reason why I make the material freely available and encourage people to go | |
through it again a week after the workshop. (This is actually specific advice | |
that I received from one of the authors of &quot;Make It Stick&quot; when I reached out to | |
them via email).</p><h2>Conclusion</h2><p>Teaching and learning are not easy. It takes a lot of work. But if you take some | |
time to learn how to learn, it might just make you a more effective learner and | |
teacher. Unfortunately, due to the limited time that I have when I&#x27;m teaching | |
(normally only a day or two), some of the principles can&#x27;t be applied as well as | |
others, but adapting and applying the principles I learned from Make It Stick | |
has been the most effective way to teach and ensure the retention of what I | |
teach. I hope this was helpful to you. Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-i-teach">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[One simple trick to optimize React re-renders]]></title> | |
<description><![CDATA[I was preparing a blog post on a subject related to React re-renders when I | |
stumbled upon this little React gem of knowledge I think you'll really | |
appreciate: https://twitter.com/kentcdodds/status/1143200604065431552 After reading this blog post…]]></description> | |
<link>https://kentcdodds.com/blog/optimize-react-re-renders</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/optimize-react-re-renders</guid> | |
<pubDate>Mon, 24 Jun 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I was preparing a blog post on a subject related to React re-renders when I | |
stumbled upon this little React gem of knowledge I think you&#x27;ll really | |
appreciate:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">If you give React the same element you gave it on the last render, it wont bother re-rendering that element.</p>— Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1143200604065431552">June 24, 2019</a></blockquote></p><p>After reading this blog post, | |
<a href="https://twitter.com/BrooksLybrand">Brooks Lybrand</a> implemented this trick and | |
this was the result:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">A little before and after optimization on a react component.<br/><br/>I didn&#x27;t use any memoization to accomplish this, yet I was able to go from a 13.4ms to a 3.6ms render.<br/><br/>I also didn&#x27;t do anything besides move code into an extra component, which ended up cutting out 27 lines of code. <a href="https://t.co/xrUN0MUm5Y">pic.twitter.com/xrUN0MUm5Y</a></p>— Brooks Lybrand (@BrooksLybrand) <a href="https://twitter.com/BrooksLybrand/status/1149800755404185609">July 12, 2019</a></blockquote></p><p>Excited? Let&#x27;s break it down with a simple contrived example and then talk | |
about what practical application this has for you in your day-to-day apps.</p><h2>An example</h2><pre><code class="language-jsx">// play with this on codesandbox: https://codesandbox.io/s/react-codesandbox-g9mt5 | |
import React from &#x27;react&#x27; | |
import ReactDOM from &#x27;react-dom&#x27; | |
function Logger(props) { | |
console.log(`${props.label} rendered`) | |
return null // what is returned here is irrelevant... | |
} | |
function Counter() { | |
const [count, setCount] = React.useState(0) | |
const increment = () =&gt; setCount(c =&gt; c + 1) | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={increment}&gt;The count is {count}&lt;/button&gt; | |
&lt;Logger label=&quot;counter&quot; /&gt; | |
&lt;/div&gt; | |
) | |
} | |
ReactDOM.render(&lt;Counter /&gt;, document.getElementById(&#x27;root&#x27;)) | |
</code></pre><p>When that&#x27;s run, &quot;counter rendered&quot; will be logged to the console initially, and | |
each time the count is incremented, &quot;counter rendered&quot; will be logged to the | |
console. This happens because when the button is clicked, state changes and | |
React needs to get the new React elements to render based on that state change. | |
When it gets those new elements, it renders and commits them to the DOM.</p><p>Here&#x27;s where things get interesting. Consider the fact that | |
<code>&lt;Logger label=&quot;counter&quot; /&gt;</code> never changes between renders. It&#x27;s static, and | |
therefore could be extracted. Let&#x27;s try that just for fun (I&#x27;m not recommending | |
you do this, wait for later in the blog post for practical recommendations).</p><pre><code class="language-jsx">// play with this on codesandbox: https://codesandbox.io/s/react-codesandbox-o9e9f | |
import React from &#x27;react&#x27; | |
import ReactDOM from &#x27;react-dom&#x27; | |
function Logger(props) { | |
console.log(`${props.label} rendered`) | |
return null // what is returned here is irrelevant... | |
} | |
function Counter(props) { | |
const [count, setCount] = React.useState(0) | |
const increment = () =&gt; setCount(c =&gt; c + 1) | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={increment}&gt;The count is {count}&lt;/button&gt; | |
{props.logger} | |
&lt;/div&gt; | |
) | |
} | |
ReactDOM.render( | |
&lt;Counter logger={&lt;Logger label=&quot;counter&quot; /&gt;} /&gt;, | |
document.getElementById(&#x27;root&#x27;), | |
) | |
</code></pre><p>Did you notice the change? Yeah! We get the initial log, but then we don&#x27;t get | |
new logs when we click the button anymore! WHAAAAT!?</p><blockquote><p>If you want to skip all the deep-dive technical details and get to the &quot;what | |
does this mean for me&quot; go ahead and <a href="#practical">plop yourself down there now</a></p></blockquote><h2>What&#x27;s going on?</h2><p>So what&#x27;s causing this difference? Well, it has to do with React elements. Why | |
don&#x27;t you take a quick break and read my blog post | |
<a href="https://kentcdodds.com/blog/what-is-jsx">&quot;What is JSX?&quot;</a> to get a quick refresher on React elements | |
and their relationship to JSX.</p><p>When React calls the counter function, it gets back something that looks a bit | |
like this:</p><pre><code class="language-javascript">// some things removed for clarity | |
const counterElement = { | |
type: &#x27;div&#x27;, | |
props: { | |
children: [ | |
{ | |
type: &#x27;button&#x27;, | |
props: { | |
onClick: increment, // this is the click handler function | |
children: &#x27;The count is 0&#x27;, | |
}, | |
}, | |
{ | |
type: Logger, // this is our logger component function | |
props: { | |
label: &#x27;counter&#x27;, | |
}, | |
}, | |
], | |
}, | |
} | |
</code></pre><p>These are called UI descriptor objects. They describe the UI that React should | |
create in the DOM (or via native components for react native). Let&#x27;s click the | |
button and take a look at the changes:</p><pre><code class="language-javascript" metastring="{8,9}">const counterElement = { | |
type: &#x27;div&#x27;, | |
props: { | |
children: [ | |
{ | |
type: &#x27;button&#x27;, | |
props: { | |
onClick: increment, | |
children: &#x27;The count is 1&#x27;, | |
}, | |
}, | |
{ | |
type: Logger, | |
props: { | |
label: &#x27;counter&#x27;, | |
}, | |
}, | |
], | |
}, | |
} | |
</code></pre><p>As far as we can tell, the only changes are the <code>onClick</code> and <code>children</code> props | |
of the <code>button</code> element. However, the entire thing is completely new! Since the | |
dawn of time using React, you&#x27;ve been creating these objects brand new on every | |
render. (Luckily, even mobile browsers are pretty fast at this, so that has | |
never been a significant performance problem).</p><p>It may actually be easier to investigate at the parts of this tree of React | |
elements is the same between renders, so here are the things that did NOT change | |
between those two renders:</p><pre><code class="language-javascript" metastring="{2,6,13,15}">const counterElement = { | |
type: &#x27;div&#x27;, | |
props: { | |
children: [ | |
{ | |
type: &#x27;button&#x27;, | |
props: { | |
onClick: increment, | |
children: &#x27;The count is 1&#x27;, | |
}, | |
}, | |
{ | |
type: Logger, | |
props: { | |
label: &#x27;counter&#x27;, | |
}, | |
}, | |
], | |
}, | |
} | |
</code></pre><p>All the element types are the same (this is typical), and the <code>label</code> prop for | |
the Logger element is unchanged. However the props object itself changes every | |
render, even though the properties of that object are the same as the previous | |
props object.</p><p><strong>Ok, here&#x27;s the kicker right here.</strong> Because the Logger props object has | |
changed, React needs to re-run the Logger function to make sure that it doesn&#x27;t | |
get any new JSX based on the new props object (in addition to effects that may | |
need to be run based on the props change). <strong>But what if we could prevent the | |
props from changing between renders?</strong> If the props don&#x27;t change, then React | |
knows that our effects shouldn&#x27;t need to be re-run and our JSX shouldn&#x27;t change | |
(<a href="https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning">because React relies on the fact that our render methods should be idempotent</a>). | |
This is exactly what React is coded to do | |
<a href="https://github.com/facebook/react/blob/d420d2ccb6223a66d5e8fe824ac0d31ed5bf87a1/packages/react-reconciler/src/ReactFiberBeginWork.js#L2571-L2575">right here</a> | |
and it&#x27;s been that way since React first started!</p><p>Ok, but the problem is that | |
<a href="https://github.com/facebook/react/blob/745baf2e061bdb7a07b511fa6fd8c1c5e8106313/packages/react/src/ReactElement.js#L177">react creates a brand new <code>props</code> object every time we create a React element</a>, | |
so how do we ensure that the props object doesn&#x27;t change between renders? | |
Hopefully now you&#x27;ve got it and you understand why the second example above | |
wasn&#x27;t re-rendering the Logger. If we create the JSX element once and re-use | |
that same one, then we&#x27;ll get the same JSX every time!</p><h2>Let&#x27;s bring it back together</h2><p>Here&#x27;s the second example again (so you don&#x27;t have to scroll back up):</p><pre><code class="language-jsx">// play with this on codesandbox: https://codesandbox.io/s/react-codesandbox-o9e9f | |
import React from &#x27;react&#x27; | |
import ReactDOM from &#x27;react-dom&#x27; | |
function Logger(props) { | |
console.log(`${props.label} rendered`) | |
return null // what is returned here is irrelevant... | |
} | |
function Counter(props) { | |
const [count, setCount] = React.useState(0) | |
const increment = () =&gt; setCount(c =&gt; c + 1) | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={increment}&gt;The count is {count}&lt;/button&gt; | |
{props.logger} | |
&lt;/div&gt; | |
) | |
} | |
ReactDOM.render( | |
&lt;Counter logger={&lt;Logger label=&quot;counter&quot; /&gt;} /&gt;, | |
document.getElementById(&#x27;root&#x27;), | |
) | |
</code></pre><p>So let&#x27;s checkout the <em>things that are the same</em> between renders:</p><pre><code class="language-javascript" metastring="{2,6,12-17}">const counterElement = { | |
type: &#x27;div&#x27;, | |
props: { | |
children: [ | |
{ | |
type: &#x27;button&#x27;, | |
props: { | |
onClick: increment, | |
children: &#x27;The count is 1&#x27;, | |
}, | |
}, | |
{ | |
type: Logger, | |
props: { | |
label: &#x27;counter&#x27;, | |
}, | |
}, | |
], | |
}, | |
} | |
</code></pre><p>Because the Logger element is completely unchanged (and therefore the props are | |
unchanged as well), React can automatically provide this optimization for us and | |
not bother re-rendering the Logger element because it shouldn&#x27;t need to be | |
re-rendered anyway. This is basically like <code>React.memo</code> except instead of | |
checking each of the props individually, React is checking the props object | |
holistically.</p><div style="position:relative"><a name="practical" style="position:absolute;top:-100px"></a></div><h2>So what does this mean for me?</h2><p>In summary, if you&#x27;re experiencing performance issues, try this:</p><ol><li>&quot;Lift&quot; the expensive component to a parent where it will be rendered less | |
often.</li><li>Then pass the expensive component down as a prop.</li></ol><p>You may find doing so solves your performance problem without needing to spread | |
<code>React.memo</code> all over you codebase like a giant intrusive band-aid 🤕😉</p><h2>Demo</h2><p>Creating a practical demo of a slow app in React is tricky because it kinda | |
requires building a full app, but I do have a contrived example app that has a | |
before/after that you can check out and play with:</p><p><iframe src="https://codesandbox.io/embed/react-codesandbox-qtdob" style="width:100%;height:500px;border-width:0px;border-radius:4px;overflow:hidden"></iframe></p><blockquote><p>One thing I want to add is that even though it&#x27;s <em>better</em> to use the faster | |
version of this code, it&#x27;s still performing really badly when it renders | |
initially and it would perform really badly if it ever actually needed to do | |
another top-down re-render (or when you update the rows/columns). That&#x27;s a | |
performance problem that should probably be dealt with on its own merits | |
(irrespective of how necessary the re-renders are). Also please remember that | |
codesandbox uses the development version of React which gives you a really | |
nice development experience, but performs WAY slower than the production | |
version of React.</p></blockquote><p>And this isn&#x27;t just something that&#x27;s useful at the top-level of your app either. | |
This could be applied to your app anywhere it makes sense. What I like about | |
this is that | |
<a href="https://twitter.com/dan_abramov/status/1143201204094341120">&quot;It&#x27;s both natural for composition <em>and</em> acts as an optimization opportunity.&quot;</a> | |
(that was Dan). So I do this naturally and get the perf benefits for free. And | |
that&#x27;s what I&#x27;ve always loved about React. <strong>React is written so that idiomatic | |
React apps are fast by default, and then React provides optimization helpers for | |
you to use as escape hatches.</strong></p><p>Good luck!</p><blockquote><p>I want to add a note that if you&#x27;re using legacy context, you wont be able to | |
get this optimization, | |
<a href="https://github.com/facebook/react/blob/d420d2ccb6223a66d5e8fe824ac0d31ed5bf87a1/packages/react-reconciler/src/ReactFiberBeginWork.js#L2576">as React has a special case for that here</a>, | |
so people concerned about performance should probably migrate from legacy | |
context.</p></blockquote> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/optimize-react-re-renders">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Colocation]]></title> | |
<description><![CDATA[We all want to have codebases that are easy to maintain, so we start out with | |
the best of intentions to make our codebase (or our corner of the codebase) | |
maintainable and easy to understand. Over time, as a codebase grows, it can | |
become more and more…]]></description> | |
<link>https://kentcdodds.com/blog/colocation</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/colocation</guid> | |
<pubDate>Mon, 17 Jun 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>We all want to have codebases that are easy to maintain, so we start out with | |
the best of intentions to make our codebase (or our corner of the codebase) | |
maintainable and easy to understand. Over time, as a codebase grows, it can | |
become more and more difficult to manage dependencies (JS, CSS, images, etc.). | |
As projects grow, an increasing amount of your codebase becomes &quot;tribal | |
knowledge&quot; (knowledge that only you or a few others are privy to) and this sort | |
of knowledge contributes to &quot;technical debt&quot; (whether that term is accurate or | |
<a href="https://twitter.com/ryanflorence/status/747983065738153985">not</a>).</p><p>I like to keep my codebases manageable for not only me (the one who wrote it), | |
but also my teammates, future maintainers, and myself in 6 months. I think we | |
can all agree that this is a great ideal that we should strive for in our | |
codebases. There are a lot of different tools and techniques at our disposal to | |
accomplish this.</p><h2>Let&#x27;s talk about Code Comments</h2><p>I don&#x27;t want to discuss whether to comment your code (you should) and what your | |
comments should be about (You explain why you&#x27;re doing something unexpected in | |
the comments so people coming after can understand the decisions that were made | |
which resulted in the unexpected or odd code). (Ok, maybe I did want to talk | |
about that a little). Instead I want to focus on where those code comments are | |
placed. We generally &quot;co-locate&quot; these comments with the code they&#x27;re explaining | |
by putting it as close as possible to the relevant code.</p><p>Consider for a minute, if we did this differently. What if we place those | |
comments in a totally separate file. A massive <code>DOCUMENTATION.md</code> file or | |
perhaps even a <code>docs/</code> directory that maps back to our <code>src/</code> directory. Sound | |
like fun to you? Yeah, not to me either. There would be some serious problems | |
we&#x27;d encounter by not co-locating our comments with the code it&#x27;s explaining.</p><ul><li><strong>Maintainability:</strong> They&#x27;d get out of sync or out of date quicker (than they | |
already do). We&#x27;d move or delete a <code>src/</code> file without updating the | |
corresponding <code>docs/</code> file.</li><li><strong>Applicability:</strong> People looking at the code in <code>src/</code> might miss an | |
important comment in <code>docs/</code> or not comment their own code because they don&#x27;t | |
realize that there&#x27;s an existing <code>docs/</code> file for the <code>src/</code> file they&#x27;re | |
editing.</li><li><strong>Ease of use:</strong> Context switching from one location to the next would be a | |
challenge with this kind of a set up as well. Having to deal with multiple | |
locations for files could make it difficult to ensure you have everything you | |
need to maintain a component.</li></ul><p>We could definitely come up with a convention for this kind of code commenting | |
style, but why would we want to? Isn&#x27;t it simpler to keep the comments | |
co-located with the code they&#x27;re explaining?</p><h2>So what?</h2><p>Now, you&#x27;re probably thinking to yourself: &quot;Yeah, duh, this is why nobody does | |
this <code>docs/</code> thing and everyone just co-locates their comments with the code. | |
That&#x27;s obvious. What&#x27;s your point?&quot; My point is that the benefits of co-location | |
are everywhere.</p><h3>HTML/View</h3><p>Take HTML for example. All the benefits of co-locating our comments translate | |
over to our templates as well. Before modern frameworks like React, you&#x27;d have | |
your view logic and your view templates in totally separate directories. This | |
falls prey to the same problems described above. These days it&#x27;s far more common | |
to put these things <strong>in the exact same file</strong> with React and Vue for example. | |
With Angular if it&#x27;s not in the same file, the template file is at least right | |
next to the JS file with which it is associated.</p><h3>CSS</h3><p>Another concept this applies well to is CSS. I&#x27;m not going to argue with you | |
about the merits of CSS-in-JS (it&#x27;s fantastic), but the benefits are out of this | |
world. | |
<a href="https://medium.com/seek-blog/a-unified-styling-language-d0c208de2660">Learn more here</a>.</p><h3>Tests</h3><p>This concept of file co-location applies great to unit tests as well. How common | |
is it to find a project with a <code>src/</code> directory and a <code>test/</code> directory filled | |
with unit tests that attempts to mirror the <code>src/</code> directory? All the pitfalls | |
described above apply here as well. I probably wouldn&#x27;t go as far as putting the | |
unit tests in the exact same file, but I don&#x27;t totally rule that out as an | |
interesting idea either (the implementation is left as an exercise to the | |
reader).</p><p>To help enable a more maintainable codebase, we should co-locate our tests files | |
with the file or group of files they are testing. This ensures that when new | |
people (or myself in 6 months) come to the code, they can see immediately that | |
the module is tested and use those tests as a reference to learn about the | |
module. When they make changes, it reminds them to update (add/remove/modify) | |
the tests to account for their changes.</p><h3>State</h3><p>Application/Component state experiences the same benefits. The more | |
disconnected/indirect your state is from the UI that&#x27;s using it, the harder it | |
is to maintain. Localizing state has even more benefits than maintainability, it | |
also improves the performance of your application. A state change in one corner | |
of your application component tree will re-render a lot fewer components than a | |
state change at the top of the tree. Localize your state.</p><h3>&quot;Reusable&quot; utility files</h3><p>This applies to &quot;utility&quot; files and functions as well. Imagine you&#x27;re writing a | |
component and see a nice bit of code that could be extracted into its own | |
function. You extract it and think: &quot;Huh... I&#x27;ll bet a lot of people could use | |
this.&quot; So you pull it out and put it into your app&#x27;s <code>utils/</code> directory and move | |
on with your life.</p><p>Later, your component is deleted, but the utility you wrote is out of sight, out | |
of mind and it remains (along with its tests) because the person who deleted it | |
assumed it was more widely used. Over the years, engineers work hard to make | |
sure that the function and its tests continue to run and function properly | |
without even realizing that it&#x27;s no longer needed at all. Wasted effort and | |
cognitive load.</p><p>If instead you had just left that function directly in the file that used it, | |
the story would be completely different. I&#x27;m not saying don&#x27;t bother unit | |
testing complex utility functions (please do), but keeping them closer to where | |
they&#x27;re needed helps you avoid problems.</p><blockquote><p>And for heaven&#x27;s sake, | |
<a href="https://github.com/yannickcr/eslint-plugin-react/blob/e6b4c33a1db4cc94c3e9223b09fb92b1dbddc00d/docs/rules/no-multi-comp.md">please DELETE THIS ESLINT RULE</a> | |
and all rules like it.</p></blockquote><h2>The principle</h2><p><strong>The concept of co-location can be boiled down to this fundamental principle:</strong></p><blockquote><p>Place code as close to where it&#x27;s relevant as possible</p></blockquote><p>You might also say: &quot;Things that change together should be located as close as | |
reasonable.&quot; (<a href="https://twitter.com/dan_abramov">Dan Abramov</a> said something like | |
this to me once).</p><h2>Open Source made easy(-er)</h2><p>Aside from avoiding the problems discussed earlier, there are other benefits to | |
structuring your projects this way. Taking a component and turning it into an | |
open source project is often as simple as copy/pasting the folder to another | |
project and publishing that to npm. Then you just install it in your project and | |
update your require/import statements and you&#x27;re good to go.</p><h2>Exceptions</h2><p>Sure there&#x27;s a good argument for documentation that spans the whole or part of a | |
system and how things integrate together. And where would you put integration or | |
end-to-end tests that span across components? <em>You might think those are | |
exceptions</em>, but they can actually subscribe nicely to the principle mentioned | |
above</p><p>If I have a part of my app associated with user authentication and I want to | |
document that flow, I can put a <em>README.md</em> file in the folder that has all of | |
the modules associated with user authentication. If I need to write integration | |
tests for that flow, I could place the file for those tests in that same folder.</p><p>For end-to-end tests, those generally make more sense to go at the root of the | |
project. They span beyond the project itself and into other parts of the system, | |
so it makes sense to me for those to be in a separate directory. They don&#x27;t | |
really map to the <code>src/</code> files. In fact, E2E tests don&#x27;t really care how the | |
<code>src/</code> is organized at all. Refactoring and moving around files in the <code>src/</code> | |
directory should not necessitate changing the E2E tests at all.</p><h2>Conclusion</h2><p>Our goal here is to build software that is as simple to maintain as possible. | |
The same benefits of <strong>maintainability</strong>, <strong>applicability</strong>, and <strong>ease of use</strong> | |
we get from co-locating our comments we get by co-location of other things as | |
well. If you&#x27;ve never tried it out, I recommend you give it a shot.</p><p>P.S. If you&#x27;re concerned about violating &quot;separation of concerns&quot; I recommend | |
you check out <a href="https://youtu.be/x7cQ3mrcKaY">this talk</a> by | |
<a href="https://twitter.com/floydophone">Pete Hunt</a> and re-evaluate what that means 😀.</p><p>P.P.S. I should also note that this applies great to images and really any other | |
resource as well. And when you use a tool like | |
<a href="https://webpack.js.org">webpack</a>, co-locating those resources is crazy easy | |
too. Honestly, this is one of the core value propositions of webpack IMO.</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/colocation">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[When to useMemo and useCallback]]></title> | |
<description><![CDATA[Current Available Translations: Korean Here's a candy dispenser: Here's how it's implemented: Now I want to ask you a question and I want you to think hard about it before | |
moving forward. I'm going to make a change to this and I want you to tell me…]]></description> | |
<link>https://kentcdodds.com/blog/usememo-and-usecallback</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/usememo-and-usecallback</guid> | |
<pubDate>Tue, 04 Jun 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Current Available Translations:</p><ul><li><a href="https://ideveloper2.dev/blog/2019-06-14--when-to-use-memo-and-use-callback">Korean</a></li></ul><p>Here&#x27;s a candy dispenser:</p><div style="background:white;padding:20px;border:2px solid black;border-radius:5px;margin-bottom:20px"><div><h1>Candy Dispenser</h1><div><div>Available Candy</div><ul><li><button>grab</button> snickers</li><li><button>grab</button> skittles</li><li><button>grab</button> twix</li><li><button>grab</button> milky way</li></ul></div></div></div><p>Here&#x27;s how it&#x27;s implemented:</p><pre><code class="language-jsx">function CandyDispenser() { | |
const initialCandies = [&#x27;snickers&#x27;, &#x27;skittles&#x27;, &#x27;twix&#x27;, &#x27;milky way&#x27;] | |
const [candies, setCandies] = React.useState(initialCandies) | |
const dispense = candy =&gt; { | |
setCandies(allCandies =&gt; allCandies.filter(c =&gt; c !== candy)) | |
} | |
return ( | |
&lt;div&gt; | |
&lt;h1&gt;Candy Dispenser&lt;/h1&gt; | |
&lt;div&gt; | |
&lt;div&gt;Available Candy&lt;/div&gt; | |
{candies.length === 0 ? ( | |
&lt;button onClick={() =&gt; setCandies(initialCandies)}&gt;refill&lt;/button&gt; | |
) : ( | |
&lt;ul&gt; | |
{candies.map(candy =&gt; ( | |
&lt;li key={candy}&gt; | |
&lt;button onClick={() =&gt; dispense(candy)}&gt;grab&lt;/button&gt; {candy} | |
&lt;/li&gt; | |
))} | |
&lt;/ul&gt; | |
)} | |
&lt;/div&gt; | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>Now I want to ask you a question and I want you to think hard about it before | |
moving forward. I&#x27;m going to make a change to this and I want you to tell me | |
which will have the better performance characteristics.</p><p>The only thing I&#x27;m going to change is wrap the <code>dispense</code> function inside | |
<code>React.useCallback</code>:</p><pre><code class="language-javascript">const dispense = React.useCallback(candy =&gt; { | |
setCandies(allCandies =&gt; allCandies.filter(c =&gt; c !== candy)) | |
}, []) | |
</code></pre><p>Here&#x27;s the original again:</p><pre><code class="language-javascript">const dispense = candy =&gt; { | |
setCandies(allCandies =&gt; allCandies.filter(c =&gt; c !== candy)) | |
} | |
</code></pre><p>So here&#x27;s my question, in this specific case, which of these is better for | |
performance? Go ahead and submit your guess (this is not recorded anywhere):</p><div style="margin:20px 0 50px 0"><div><div style="margin-bottom:10px"><button>original</button></div><div><button>useCallback</button></div></div></div><blockquote><p>Let me give you some space to not spoil the answer for you...</p></blockquote><div style="height:400px"></div><blockquote><p>Keep scrolling... You did answer, didn&#x27;t you?</p></blockquote><div style="height:400px"></div><blockquote><p>There, that should do it...</p></blockquote><h2>Why is <code>useCallback</code> worse?!</h2><p>We hear a lot that you should use <code>React.useCallback</code> to improve performance and | |
that &quot;inline functions can be problematic for performance,&quot; so how could it ever | |
be better to <em>not</em> <code>useCallback</code>?</p><p>Just take a step back from our specific example, and even from React and | |
consider this: <strong>Every line of code which is executed comes with a cost.</strong> Let | |
me refactor the <code>useCallback</code> example a bit (no actual changes, just moving | |
things around) to illustrate things more clearly:</p><pre><code class="language-javascript">const dispense = candy =&gt; { | |
setCandies(allCandies =&gt; allCandies.filter(c =&gt; c !== candy)) | |
} | |
const dispenseCallback = React.useCallback(dispense, []) | |
</code></pre><p>And here&#x27;s the original again:</p><pre><code class="language-javascript">const dispense = candy =&gt; { | |
setCandies(allCandies =&gt; allCandies.filter(c =&gt; c !== candy)) | |
} | |
</code></pre><p>Notice anything about these? Let&#x27;s look at the diff:</p><pre><code class="language-diff"> const dispense = candy =&gt; { | |
setCandies(allCandies =&gt; allCandies.filter(c =&gt; c !== candy)) | |
} | |
+ const dispenseCallback = React.useCallback(dispense, []) | |
</code></pre><p>Yeah, they&#x27;re <em>exactly</em> the same except the <code>useCallback</code> version is doing | |
<em>more</em> work. Not only do we have to define the function, but we also have to | |
define an array (<code>[]</code>) <em>and</em> call the <code>React.useCallback</code> which itself is | |
setting properties/running through logical expressions etc.</p><p>So in <em>both</em> cases JavaScript must allocate memory for the function definition | |
on every render and depending on how <code>useCallback</code> is implemented, you may get | |
<em>more</em> allocation for function definitions (this is actually not the case, but | |
the point still stands). This is what I was trying to get across with my twitter | |
poll here:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Assuming this code appears in a React function component, how many function allocations are happening with this code on each render?<br/><br/>const a = () =&gt; {}<br/><br/>And how many are happening with this code?<br/><br/>const a = useCallback(() =&gt; {}, [])</p>— Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1135943012410830848">June 4, 2019</a></blockquote></p><blockquote><p>Granted, I had several people tell me that was worded poorly, so my apologies | |
if you got the wrong answer but actually knew the correct answer.</p></blockquote><p>I&#x27;d like to mention also that on the second render of the component, the | |
original <code>dispense</code> function gets garbage collected (freeing up memory space) | |
and then a new one is created. However with <code>useCallback</code> the original | |
<code>dispense</code> function wont get garbage collected and a new one is created, so | |
you&#x27;re worse-off from a memory perspective as well.</p><p>As a related note, if you have dependencies then it&#x27;s quite possible React is | |
hanging on to a reference to previous functions because memoization typically | |
means that we keep copies of old values to return in the event we get the same | |
dependencies as given previously. The especially astute of you will notice that | |
this means React also has to hang on to a reference to the dependencies for this | |
equality check (which incidentally is probably happening anyway thanks to your | |
closure, but it&#x27;s something worth mentioning anyway).</p><h2>How is <code>useMemo</code> different, but similar?</h2><p><code>useMemo</code> is similar to <code>useCallback</code> except it allows you to apply memoization | |
to any value type (not just functions). It does this by accepting a function | |
which returns the value and then that function is <em>only</em> called when the value | |
needs to be retrieved (which typically will only happen once each time an | |
element in the dependencies array changes between renders).</p><p>So, if I didn&#x27;t want to initialize that array of <code>initialCandies</code> every render, | |
I could make this change:</p><pre><code class="language-diff">- const initialCandies = [&#x27;snickers&#x27;, &#x27;skittles&#x27;, &#x27;twix&#x27;, &#x27;milky way&#x27;] | |
+ const initialCandies = React.useMemo( | |
+ () =&gt; [&#x27;snickers&#x27;, &#x27;skittles&#x27;, &#x27;twix&#x27;, &#x27;milky way&#x27;], | |
+ [], | |
+ ) | |
</code></pre><p>And I would avoid that problem, but the savings would be so minimal that the | |
cost of making the code more complex just isn&#x27;t worth it. In fact, it&#x27;s probably | |
worse to use <code>useMemo</code> for this as well because again we&#x27;re making a function | |
call and that code is doing property assignments etc.</p><p>In this particular scenario, what would be even better is to make this change:</p><pre><code class="language-diff">+ const initialCandies = [&#x27;snickers&#x27;, &#x27;skittles&#x27;, &#x27;twix&#x27;, &#x27;milky way&#x27;] | |
function CandyDispenser() { | |
- const initialCandies = [&#x27;snickers&#x27;, &#x27;skittles&#x27;, &#x27;twix&#x27;, &#x27;milky way&#x27;] | |
const [candies, setCandies] = React.useState(initialCandies) | |
</code></pre><p>But sometimes you don&#x27;t have that luxury because the value is either derived | |
from <code>props</code> or other variables initialized within the body of the function.</p><p>The point is that it doesn&#x27;t matter either way. The benefits of optimizing that | |
code is so minuscule that your time would be WAY better spent worrying about | |
making your product better.</p><h2>What&#x27;s the point?</h2><p>The point is this:</p><p><strong>Performance optimizations are not free. They ALWAYS come with a cost but do | |
NOT always come with a benefit to offset that cost.</strong></p><p>Therefore, <em>optimize responsibly</em>.</p><h2>So when <em>should</em> I <code>useMemo</code> and <code>useCallback</code>?</h2><p>There are specific reasons both of these hooks are built-into React:</p><ol><li>Referential equality</li><li>Computationally expensive calculations</li></ol><h2>Referential equality</h2><p>If you&#x27;re new to JavaScript/programming, it wont take long before you learn why | |
this is the case:</p><pre><code class="language-javascript">true === true // true | |
false === false // true | |
1 === 1 // true | |
&#x27;a&#x27; === &#x27;a&#x27; // true | |
{} === {} // false | |
[] === [] // false | |
() =&gt; {} === () =&gt; {} // false | |
const z = {} | |
z === z // true | |
// NOTE: React actually uses Object.is, but it&#x27;s very similar to === | |
</code></pre><p>I&#x27;m not going to go too deep into this, but suffice it to say when you define an | |
object inside your React function component, it is <em>not</em> going to be | |
referentially equal to the last time that same object was defined (even if it | |
has all the same properties with all the same values).</p><p>There are two situations where referential equality matters in React, let&#x27;s go | |
through them one at a time.</p><h3>Dependencies lists</h3><p>Let&#x27;s review an example.</p><blockquote><p>Warning, you&#x27;re about to see some seriously contrived code. Please don&#x27;t | |
nit-pick that and just focus on the concepts please, thank you.</p></blockquote><pre><code class="language-jsx">function Foo({bar, baz}) { | |
const options = {bar, baz} | |
React.useEffect(() =&gt; { | |
buzz(options) | |
}, [options]) // we want this to re-run if bar or baz change | |
return &lt;div&gt;foobar&lt;/div&gt; | |
} | |
function Blub() { | |
return &lt;Foo bar=&quot;bar value&quot; baz={3} /&gt; | |
} | |
</code></pre><p>The reason this is problematic is because <code>useEffect</code> is going to do a | |
referential equality check on <code>options</code> between every render, and thanks to the | |
way JavaScript works, <code>options</code> will be new every time so when React tests | |
whether <code>options</code> changed between renders it&#x27;ll always evaluate to <code>true</code>, | |
meaning the <code>useEffect</code> callback will be called after every render rather than | |
only when <code>bar</code> and <code>baz</code> change.</p><p>There are two things we can do to fix this:</p><pre><code class="language-jsx">// option 1 | |
function Foo({bar, baz}) { | |
React.useEffect(() =&gt; { | |
const options = {bar, baz} | |
buzz(options) | |
}, [bar, baz]) // we want this to re-run if bar or baz change | |
return &lt;div&gt;foobar&lt;/div&gt; | |
} | |
</code></pre><p>That&#x27;s a great option and if this were a real thing that&#x27;s how I&#x27;d fix this.</p><p>But there&#x27;s one situation when this isn&#x27;t a practical solution: If <code>bar</code> or | |
<code>baz</code> are (non-primitive) objects/arrays/functions/etc:</p><pre><code class="language-jsx">function Blub() { | |
const bar = () =&gt; {} | |
const baz = [1, 2, 3] | |
return &lt;Foo bar={bar} baz={baz} /&gt; | |
} | |
</code></pre><p>This is precisely the reason why <code>useCallback</code> and <code>useMemo</code> exist. So here&#x27;s | |
how you&#x27;d fix that (all together now):</p><pre><code class="language-jsx">function Foo({bar, baz}) { | |
React.useEffect(() =&gt; { | |
const options = {bar, baz} | |
buzz(options) | |
}, [bar, baz]) | |
return &lt;div&gt;foobar&lt;/div&gt; | |
} | |
function Blub() { | |
const bar = React.useCallback(() =&gt; {}, []) | |
const baz = React.useMemo(() =&gt; [1, 2, 3], []) | |
return &lt;Foo bar={bar} baz={baz} /&gt; | |
} | |
</code></pre><blockquote><p>Note that this same thing applies for the dependencies array passed to | |
<code>useEffect</code>, <code>useLayoutEffect</code>, <code>useCallback</code>, and <code>useMemo</code>.</p></blockquote><h3><code>React.memo</code> (and friends)</h3><blockquote><p>Warning, you&#x27;re about to see some more contrived code. Please be advised to | |
not nit-pick this either but focus on the concepts, thanks.</p></blockquote><p>Check this out:</p><pre><code class="language-jsx">function CountButton({onClick, count}) { | |
return &lt;button onClick={onClick}&gt;{count}&lt;/button&gt; | |
} | |
function DualCounter() { | |
const [count1, setCount1] = React.useState(0) | |
const increment1 = () =&gt; setCount1(c =&gt; c + 1) | |
const [count2, setCount2] = React.useState(0) | |
const increment2 = () =&gt; setCount2(c =&gt; c + 1) | |
return ( | |
&lt;&gt; | |
&lt;CountButton count={count1} onClick={increment1} /&gt; | |
&lt;CountButton count={count2} onClick={increment2} /&gt; | |
&lt;/&gt; | |
) | |
} | |
</code></pre><p>Every time you click on either of those buttons, the <code>DualCounter</code>&#x27;s state | |
changes and therefore re-renders which in turn will re-render both of the | |
<code>CountButton</code>s. However, the only one that <em>actually</em> needs to re-render is the | |
one that was clicked right? So if you click the first one, the second one gets | |
re-rendered, but nothing changes. We call this an &quot;unnecessary re-render.&quot;</p><p><strong>MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS.</strong> | |
React is VERY fast and there are so many things I can think of for you to do | |
with your time that would be better than optimizing things like this. In fact, | |
the need to optimize stuff with what I&#x27;m about to show you is so rare that I&#x27;ve | |
literally <em>never</em> needed to do it in the 3 years I worked on PayPal products and | |
the even longer time that I&#x27;ve been working with React.</p><p>However, there are situations when rendering can take a substantial amount of | |
time (think highly interactive Graphs/Charts/Animations/etc.). Thanks to the | |
pragmatistic nature of React, there&#x27;s an escape hatch:</p><pre><code class="language-jsx">const CountButton = React.memo(function CountButton({onClick, count}) { | |
return &lt;button onClick={onClick}&gt;{count}&lt;/button&gt; | |
}) | |
</code></pre><p>Now React will only re-render <code>CountButton</code> when it&#x27;s props change! Woo! But | |
we&#x27;re not done yet. Remember that whole referential equality thing? In the | |
<code>DualCounter</code> component, we&#x27;re defining the <code>increment1</code> and <code>increment2</code> | |
functions within the component functions which means every time <code>DualCounter</code> is | |
re-rendered, those functions will be new and therefore React will re-render both | |
of the <code>CountButton</code>s anyway.</p><p>So this is the other situation where <code>useCallback</code> and <code>useMemo</code> can be of help:</p><pre><code class="language-jsx" metastring="{7, 10}">const CountButton = React.memo(function CountButton({onClick, count}) { | |
return &lt;button onClick={onClick}&gt;{count}&lt;/button&gt; | |
}) | |
function DualCounter() { | |
const [count1, setCount1] = React.useState(0) | |
const increment1 = React.useCallback(() =&gt; setCount1(c =&gt; c + 1), []) | |
const [count2, setCount2] = React.useState(0) | |
const increment2 = React.useCallback(() =&gt; setCount2(c =&gt; c + 1), []) | |
return ( | |
&lt;&gt; | |
&lt;CountButton count={count1} onClick={increment1} /&gt; | |
&lt;CountButton count={count2} onClick={increment2} /&gt; | |
&lt;/&gt; | |
) | |
} | |
</code></pre><p>Now we can avoid the so-called &quot;unnecessary re-renders&quot; of <code>CountButton</code>.</p><p>I would like to re-iterate that I strongly advise against using <code>React.memo</code> (or | |
it&#x27;s friends <code>PureComponent</code> and <code>shouldComponentUpdate</code>) without measuring | |
because those optimizations come with a cost and you need to make sure you know | |
what that cost will be as well as the associated benefit so you can determine | |
whether it will actually be helpful (and not harmful) in your case, and as we | |
observe above <strong>it can be tricky to get right all the time so you may not be | |
reaping any benefits at all anyway.</strong></p><h2>Computationally expensive calculations</h2><p>This is the other reason that <code>useMemo</code> is a built-in hook for React (note that | |
this one does not apply to <code>useCallback</code>). The benefit to <code>useMemo</code> is that you | |
can take a value like:</p><pre><code class="language-javascript">const a = {b: props.b} | |
</code></pre><p>And get it lazily:</p><pre><code class="language-javascript">const a = React.useMemo(() =&gt; ({b: props.b}), [props.b]) | |
</code></pre><p>This isn&#x27;t really useful for that case above, but imagine that you&#x27;ve got a | |
function that synchronously calculates a value which is computationally | |
expensive to calculate (I mean how many apps actually need to | |
<a href="https://developer.mozilla.org/en-US/docs/Tools/Performance/Scenarios/Intensive_JavaScript">calculate prime numbers like this</a> | |
ever, but that&#x27;s an example):</p><pre><code class="language-jsx">function RenderPrimes({iterations, multiplier}) { | |
const primes = calculatePrimes(iterations, multiplier) | |
return &lt;div&gt;Primes! {primes}&lt;/div&gt; | |
} | |
</code></pre><p>That could be pretty slow given the right <code>iterations</code> or <code>multiplier</code> and | |
there&#x27;s not too much you can do about that specifically. You can&#x27;t automagically | |
make your user&#x27;s hardware faster. But you <em>can</em> make it so you never have to | |
calculate the same value twice in a row, which is what <code>useMemo</code> will do for | |
you:</p><pre><code class="language-jsx">function RenderPrimes({iterations, multiplier}) { | |
const primes = React.useMemo(() =&gt; calculatePrimes(iterations, multiplier), [ | |
iterations, | |
multiplier, | |
]) | |
return &lt;div&gt;Primes! {primes}&lt;/div&gt; | |
} | |
</code></pre><p>The reason this works is because even though you&#x27;re defining the function to | |
calculate the primes on every render (which is VERY fast), React is only calling | |
that function when the value is needed. On top of that React also stores | |
previous values given the inputs and will return the previous value given the | |
same previous inputs. That&#x27;s memoization at work.</p><h2>Conclusion</h2><p>I&#x27;d just like to wrap this up by saying that every abstraction (and performance | |
optimization) comes at a cost. Apply | |
<a href="https://kentcdodds.com/blog/aha-programming">the AHA Programming principle</a> and wait until the | |
abstraction/optimization is screaming at you before applying it and you&#x27;ll save | |
yourself from incurring the costs without reaping the benefit.</p><p>Specifically the cost for <code>useCallback</code> and <code>useMemo</code> are that you make the code | |
more complex for your co-workers, you could make a mistake in the dependencies | |
array, and you&#x27;re potentially making performance worse by invoking the built-in | |
hooks and preventing dependencies and memoized values from being garbage | |
collected. Those are all fine costs to incur if you get the performance benefits | |
necessary, but <strong>it&#x27;s best to measure first.</strong></p><p>Related reading:</p><ul><li>React FAQ: | |
<a href="https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render">&quot;Are Hooks slow because of creating functions in render?&quot;</a></li><li><a href="https://twitter.com/ryanflorence">Ryan Florence</a>: | |
<a href="https://reacttraining.com/blog/react-inline-functions-and-performance">React, Inline Functions, and Performance</a></li></ul><p>P.S. If you&#x27;re among the few who worry about the move to hooks and that it | |
forces us to define functions within our function components where we used to | |
define functions as methods on our classes, I would invite you to consider the | |
fact that we&#x27;ve been defining methods in the render phase of our components | |
since day one... For example:</p><pre><code class="language-jsx">class FavoriteNumbers extends React.Component { | |
render() { | |
return ( | |
&lt;ul&gt; | |
{this.props.favoriteNumbers.map(number =&gt; ( | |
// TADA! This is a function defined in the render method! | |
// Hooks did not introduce this concept. | |
// We&#x27;ve been doing this all along. | |
&lt;li key={number}&gt;{number}&lt;/li&gt; | |
))} | |
&lt;/ul&gt; | |
) | |
} | |
} | |
</code></pre> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/usememo-and-usecallback">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[What open source project should I contribute to?]]></title> | |
<description><![CDATA[This is a question I've had countless times: https://twitter.com/sarna_pranu/status/672438850724175872 Pranu first pull request soon after tweeting this https://twitter.com/geraldchecka/status/670445392706736128 And in direct messages, emails, etc…]]></description> | |
<link>https://kentcdodds.com/blog/what-open-source-project-should-i-contribute-to</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/what-open-source-project-should-i-contribute-to</guid> | |
<pubDate>Mon, 03 Jun 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>This is a question I&#x27;ve had countless times:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr"><a href="https://twitter.com/kentcdodds">@kentcdodds</a> hey Kent! went through your post on Get started contributing to open source, Wanting to make some contributions. Guide me master</p>— Pranu Sarna (@sarna_pranu) <a href="https://twitter.com/sarna_pranu/status/672438850724175872">December 3, 2015</a></blockquote></p><p><em><a href="https://github.com/Automattic/mongoose/pull/3644">Pranu first pull request soon after tweeting this</a></em></p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr"><a href="https://twitter.com/_ericelliott">@_ericelliott</a> &amp; <a href="https://twitter.com/kentcdodds">@kentcdodds</a> Can you suggest some frontend repos to contribute to? <a href="https://t.co/WjrLWiRAwT">https://t.co/WjrLWiRAwT</a></p>— Anvesh (@geraldchecka) <a href="https://twitter.com/geraldchecka/status/670445392706736128">November 28, 2015</a></blockquote></p><p>And in direct messages, emails, etc. The general gist of it is: &quot;What open | |
source project can you recommend I start contributing to?&quot; Many of these people | |
read my <a href="https://kentcdodds.com/blog/first-timers-only">First Timers Only</a> post and are hoping to find | |
a project that is friendly to newcomers | |
<a href="https://help.github.com/articles/creating-a-pull-request">making</a> | |
<a href="https://help.github.com/articles/using-pull-requests">pull requests</a>.</p><h2>The Answer</h2><p>My silver bullet answer comes from my blog post | |
<a href="https://kentcdodds.com/blog/open-source-stamina">Open Source Stamina</a>:</p><blockquote><p>You contribute best to something you use regularly</p></blockquote><p>Where I&#x27;ve found the most satisfaction out of contributing to open source is in | |
projects that matter to me and (possibly) others. And then contributing to that | |
project regularly. To do that you need to have an understanding of the use cases | |
and pains associated with a particular tool or library. This is why I say <em>it&#x27;s | |
best to contribute to something you use regularly</em>.</p><p>What open source libraries/frameworks/tools do you use regularly? Perhaps you&#x27;re | |
working with Webpack and feel like a configuration option could be improved or | |
documented better. Or maybe you&#x27;re working with a React or Vue library that | |
could use a little polish. One thing&#x27;s certain, whatever you&#x27;re building, you&#x27;re | |
probably using an open source project or tool that you could personally benefit | |
from contributing to.</p><p>Step 1: Open your <code>package.json</code> and read through the dependencies you have. | |
Think back on your experience learning and using that module. Remember | |
struggling with one of them? Pick that one.</p><h2>Contributing</h2><p>Once you&#x27;ve found the project you want to contribute to, how do you know what to | |
contribute? Many projects have | |
<a href="https://github.com/blog/1184-contributing-guidelines">a CONTRIBUTING file</a>. | |
Look for that first to find instructions for contributing to the project. If | |
there isn&#x27;t one, there may be instructions in the <code>README</code> (normally shown on | |
the homepage of the project). If there aren&#x27;t any such instructions, you might | |
submit a pull request to add just a skeleton <code>CONTRIBUTING.md</code> file to start a | |
conversation about adding one.</p><p>Familiarize yourself with the project. Reading documentation is good, but my | |
favorite way to learn how a project works is by reading the code. My favorite | |
way to do this is by sticking a <code>debugger</code> before I call a library function or | |
when a library calls my function and jumping around the call stack, like this:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">If you&#x27;ve not stepped in React code in the debugger, give it a shot, it&#x27;s kinda fun :) <a href="https://t.co/R84yJfjLT8">https://t.co/R84yJfjLT8</a> <a href="https://t.co/KEA99HMIVK">pic.twitter.com/KEA99HMIVK</a></p>— Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1135687475110670341">June 3, 2019</a></blockquote></p><p>Step through the code and you&#x27;ll learn a lot about how the framework/library | |
works. Don&#x27;t worry if you don&#x27;t understand what&#x27;s going on right away. That will | |
come with time. Keep at it. You can do it! You can do this same thing with | |
non-browser based tools with your favorite | |
<a href="https://code.visualstudio.com/docs/editor/debugging">node debugger</a> (or add | |
console.logs).</p><p>Once you&#x27;ve figured out the standards and processes for contributing to the | |
project and familiarized yourself with its inner-workings a bit, you&#x27;ll need to | |
identify the changes that the project needs. I recommend you look at existing | |
issues and comment on ones you think are interesting. Work with the | |
maintainer(s) to identify a good implementation and | |
<a href="https://help.github.com/articles/creating-a-pull-request">make your pull request</a>!</p><p>If you have your own idea of a bug fix or a feature you want to implement, I | |
strongly recommend you run it by the project maintainer(s) in a | |
<a href="https://guides.github.com/features/issues">GitHub issue</a> first. Perhaps they&#x27;ll | |
say it&#x27;s out of scope for the project or they&#x27;re working on it, or they could | |
give you some direction. You&#x27;ll waste less time by making sure your pull request | |
will be accepted before you make it (just like how I was certain my wife would | |
answer &quot;yes&quot; when I asked her to marry me <em>before</em> I asked 😃).</p><p><em>Also, see <a href="http://24pullrequests.com/contributing">this page</a> for more tips on | |
contributing.</em></p><h2>Your First Pull Request</h2><p>For your first | |
<a href="https://help.github.com/articles/using-pull-requests">pull request</a>, feel free | |
to just find a random project out there with a good first timer bug/feature and | |
try your hand at contributing. Let the project maintainer know that you&#x27;re new | |
and are wanting some guidance to learn how to get into it. Maybe they&#x27;re too | |
busy to help, if so, move on and find another project. That first contribution | |
is the hardest, you may want some help and coaching. The actual code | |
contribution matters less than learning the process. So find a project or | |
someone who has time and patience to mentor you.</p><p>You might also be interested in watching my <strong>free</strong> egghead.io course | |
<a href="https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github">How to Contribute to an Open Source Project on GitHub</a>:</p><p><a href="https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github"><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:100%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Course Artwork" title="Course Artwork" src="https://kentcdodds.com/static/0aa5a059d464b7bd7b3912ac9a7d8be9/8ff1e/pull-request-art.png" srcSet="https://kentcdodds.com/static/0aa5a059d464b7bd7b3912ac9a7d8be9/f4a45/pull-request-art.png 259w,https://kentcdodds.com/static/0aa5a059d464b7bd7b3912ac9a7d8be9/ef0f6/pull-request-art.png 518w,https://kentcdodds.com/static/0aa5a059d464b7bd7b3912ac9a7d8be9/8ff1e/pull-request-art.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</span></a></p><h2>Resources</h2><p>Take a look at GitHub&#x27;s issues for issues labeled | |
<a href="https://github.com/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+label%3Afirst-timers-only">first-timers-only</a>, | |
<a href="https://github.com/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+label%3A%22good+for+beginners%22+">good for beginners</a>, | |
<a href="https://github.com/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+label%3A%22good+first+bug%22+">good first bug</a> | |
(or | |
<a href="https://github.com/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+label%3Agood-first-bug">good-first-bug</a>), | |
or | |
<a href="https://github.com/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+">help wanted</a> | |
(more <a href="https://twitter.com/kentcdodds/status/672873736974897152">here</a>... we | |
<em>need</em> to standardize on this).</p><p>Also, here are good resources for finding simple ways to contribute:</p><p><a href="https://twitter.com/yourfirstpr"><strong>Your First PR (@yourfirstpr)</strong></a></p><p><a href="https://twitter.com/first_tmrs_only"><strong>first-timers-only (@first_tmrs_only)</strong></a></p><p><a href="http://24pullrequests.com"><strong>24 Pull Requests</strong></a></p><p><a href="http://up-for-grabs.net/#"><strong>Up For Grabs</strong></a></p><p><a href="https://github.com/MunGell/awesome-for-beginners"><strong>MunGell/awesome-for-beginners</strong></a></p><p><a href="http://firstpr.me/#kentcdodds">My first PR</a> was to fix a typo in a comment | |
(<a href="http://firstpr.me">find yours</a>). It was super small and it was to a project | |
that I didn&#x27;t really use all that much (discovered the typo when stepping | |
through their code in a debugger). It was a great first contribution, even | |
though I didn&#x27;t really make a lasting impact on the project and I wasn&#x27;t | |
motivated to continue contributing, it got me over the hump of contributing for | |
the first time which is the hardest part.</p><h2>Conclusion</h2><p>Contributing to open source | |
<a href="https://kentcdodds.com/blog/how-getting-into-open-source-has-been-awesome-for-me">has been awesome for me</a> | |
and I highly recommend others to get into it. It&#x27;s really hard getting started, | |
but once you get over the first contribution, making future contributions is | |
much easier. It&#x27;s not all roses. The open source community has its warts here | |
and there. Keep working at it. You&#x27;ll do great! Good luck!</p><p>By the way, if you&#x27;re interested in creating your own project, be sure to check | |
out my series on egghead.io:</p><p><a href="https://egghead.io/series/how-to-write-an-open-source-javascript-library"><strong>How to Write an Open Source JavaScript Library</strong></a></p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/what-open-source-project-should-i-contribute-to">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Avoid the Test User]]></title> | |
<description><![CDATA[The two users your UI code has are 1) The end user that's interacting with your | |
component and 2) the developer rendering your component. Imagine you have the | |
following UI (taken from my | |
Advanced React Patterns material): Note: this is not a…]]></description> | |
<link>https://kentcdodds.com/blog/avoid-the-test-user</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/avoid-the-test-user</guid> | |
<pubDate>Fri, 24 May 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>The two users your UI code has are 1) The end user that&#x27;s interacting with your | |
component and 2) the developer rendering your component. Imagine you have the | |
following UI (taken from my | |
<a href="https://kentcdodds.com/workshops/advanced-react-patterns">Advanced React Patterns</a> material):</p><div style="max-width:300px;margin:10px auto 40px auto"><div style="background-color:#ddd;border-radius:4px;padding:10px"><form><div style="margin-bottom:12px"><label style="display:block" for="username">Username</label><input id="username" name="username" disabled="" readonly="" value="jakiechan" style="width:100%"/></div><div style="margin-bottom:12px"><label style="display:block" for="tagline">Tagline</label><input id="tagline" name="tagline" value="" style="width:100%"/></div><div style="margin-bottom:12px"><label style="display:block" for="bio">Biography</label><textarea id="bio" name="bio" style="width:100%"></textarea></div><div style="display:flex;justify-content:space-between"><button type="button" disabled="">Reset</button><button type="submit" disabled="">✔</button></div></form></div></div><blockquote><p>Note: this is not a screenshot. You can actually interact with that form. | |
Hooray for <a href="https://mdxjs.com">MDX</a>!</p></blockquote><p>The form component here is called <code>&lt;UserSettings /&gt;</code>. This component exposes a | |
certain API for the developers rendering it and the users using it.</p><p><strong>The End User</strong>: Renders a username field (which is disabled because it cannot | |
be changed), tagline field, and biography field. When the end user changes one | |
of the values, the reset and submit buttons become enabled. When they click the | |
reset button the form is reset and when they click the submit button it saves | |
the user&#x27;s info (showing a loading state while we wait for the request to | |
finish). (In this demo, if you type &quot;fail&quot; in the tagline or biography then the | |
request fails and you can see the error state as well).</p><p><strong>The Developer User</strong>: They render this component within a <code>&lt;UserProvider /&gt;</code> | |
so the component can access and update the application <code>user</code> state and dispatch | |
which is stored in React context.</p><p>These are the only two users that your component should be concerned with. This | |
component can experience a lot of changes over time. If it makes changes that | |
alter the developer&#x27;s API or the end user&#x27;s expectations, then additional | |
changes need to be made. If it changes the API, (like maybe it accepts a user | |
prop instead of accessing it from context) then the developer user will have to | |
alter its usage to account for that. If it changes the user experience, then | |
maybe there will need to be release notes explaining the updates, or some | |
training material updated for example.</p><p>However, it can change in other ways too. Internal refactorings which change how | |
things are implemented (for example, to make the code easier to follow), but | |
don&#x27;t change the experience of the developer using the component or the end user | |
using it. With these kinds of changes, no additional work outside the component | |
is needed.</p><hr/><h2>The Test User</h2><p>So what does this have to do with testing? One thing that I talk about a lot is | |
&quot;<a href="https://twitter.com/kentcdodds/status/977018512689455106">The more your tests resemble the way your software is used, the more confidence they can give you.</a> &quot; | |
So knowing how your software is used is really valuable. It gives you a guide | |
for knowing how to test the component.</p><p>But far too often, I see tests which are | |
<a href="https://kentcdodds.com/blog/testing-implementation-details">testing implementation details</a> (read | |
this before continuing if you haven&#x27;t already). When you do this, you introduce | |
a third user. The developer user and the end user are really all that matters | |
for this component. So long as it serves those two, then it has a reason to | |
exist. And when you&#x27;re maintaining the component you need to keep those two | |
users in mind to make sure that if you break the contract with them, you do | |
something to handle that change.</p><p>But as soon as you start testing things which your developer user and end user | |
don&#x27;t know or care about (implementation details), you add a third testing user, | |
you&#x27;re now having to keep that third user in your head and make sure you account | |
for changes that affect the testing user as well.</p><p>And for what? To get &quot;confidence?&quot; But what are you getting confidence in when | |
you test things this way? You&#x27;re getting confidence that things work for the | |
testing user. But nobody cares about the testing user. The testing user doesn&#x27;t | |
pay the bills like the end user. It doesn&#x27;t affect the rest of the system like | |
the developer user.</p><h2>Conclusion</h2><p>Writing tests that include implementation details is all downside and no upside. | |
Focus on the developer user and the end user and your tests will actually give | |
you confidence that things will continue to work for them. When your tests break | |
it becomes a cue for you to know that you have other changes to make elsewhere | |
to account for the changes you&#x27;ve made. Avoid testing implementation details and | |
you&#x27;ll be much better off.</p><blockquote><p>P.S. If you&#x27;re interested to know how I&#x27;d test this component in a way that&#x27;s | |
free of implementation details, then you can | |
<a href="https://github.com/kentcdodds/advanced-react-patterns/blob/06a16f86d2397c4451da9faf9aeb64cbe4452ff6/src/__tests__/01.js">look at the tests here</a></p></blockquote><blockquote><p>P.S.P.S. There are definitely situations where mocking and testing | |
implementation details is necessary, read more about that in | |
<a href="https://kentcdodds.com/blog/the-merits-of-mocking">The Merits of Mocking</a></p></blockquote> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/avoid-the-test-user">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Authentication in React Applications]]></title> | |
<description><![CDATA[Skipping to the end Here's the secret to this blog post in one short code example: That's it. | |
Most apps which | |
require authentication of any kind can be drastically simplified by that one | |
little trick. Rather than trying to do something fancy to…]]></description> | |
<link>https://kentcdodds.com/blog/authentication-in-react-applications</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/authentication-in-react-applications</guid> | |
<pubDate>Mon, 20 May 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<h2>Skipping to the end</h2><p>Here&#x27;s the secret to this blog post in one short code example:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {useUser} from &#x27;./context/auth&#x27; | |
import AuthenticatedApp from &#x27;./authenticated-app&#x27; | |
import UnauthenticatedApp from &#x27;./unauthenticated-app&#x27; | |
function App() { | |
const user = useUser() | |
return user ? &lt;AuthenticatedApp /&gt; : &lt;UnauthenticatedApp /&gt; | |
} | |
export App | |
</code></pre><p>That&#x27;s it. | |
<a href="https://twitter.com/kentcdodds/status/1131184429169168387">Most apps</a> which | |
require authentication of any kind can be drastically simplified by that one | |
little trick. Rather than trying to do something fancy to redirect the user when | |
they happen to land on a page that they&#x27;re not supposed to, instead you don&#x27;t | |
render that stuff at all. Things get even cooler when you do this:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {useUser} from &#x27;./context/auth&#x27; | |
const AuthenticatedApp = React.lazy(() =&gt; import(&#x27;./authenticated-app&#x27;)) | |
const UnauthenticatedApp = React.lazy(() =&gt; import(&#x27;./unauthenticated-app&#x27;)) | |
function App() { | |
const user = useUser() | |
return user ? &lt;AuthenticatedApp /&gt; : &lt;UnauthenticatedApp /&gt; | |
} | |
export App | |
</code></pre><p>Sweet, now you don&#x27;t even bother loading the code until it&#x27;s needed. So the | |
login screen shows up faster for unauthenticated users and the app loads faster | |
for authenticated users.</p><p>What the <code>&lt;AuthenticatedApp /&gt;</code> and <code>&lt;UnauthenticatedApp /&gt;</code> do is totally up to | |
you. Maybe they render unique routers. Maybe they even use some of the same | |
components. But whatever they do, you don&#x27;t have to bother wondering whether the | |
user is logged in because you make it literally impossible to render one side of | |
the app or the other if there is no user.</p><h2>How do we get here?</h2><blockquote><p>If you want to just look at how it&#x27;s all done, then you can checkout | |
<a href="https://github.com/kentcdodds/bookshelf">the bookshelf repo</a> which I made for | |
my <a href="https://kentcdodds.com/workshops/build-react-apps">Build ReactJS Applications Workshop</a>.</p></blockquote><p>Ok, so what do you do to get to this point? Let&#x27;s start by looking at where | |
we&#x27;re actually rendering the app:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import ReactDOM from &#x27;react-dom&#x27; | |
import App from &#x27;./app&#x27; | |
import AppProviders from &#x27;./context&#x27; | |
ReactDOM.render( | |
&lt;AppProviders&gt; | |
&lt;App /&gt; | |
&lt;/AppProviders&gt;, | |
document.getElementById(&#x27;root&#x27;), | |
) | |
</code></pre><p>And here&#x27;s that <code>&lt;AppProviders /&gt;</code> component:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {AuthProvider} from &#x27;./auth-context&#x27; | |
import {UserProvider} from &#x27;./user-context&#x27; | |
function AppProviders({children}) { | |
return ( | |
&lt;AuthProvider&gt; | |
&lt;UserProvider&gt;{children}&lt;/UserProvider&gt; | |
&lt;/AuthProvider&gt; | |
) | |
} | |
export default AppProviders | |
</code></pre><p>Ok, cool, so we have a provider from the app&#x27;s authentication and one for the | |
user&#x27;s data. So presumably the <code>&lt;AuthProvider /&gt;</code> would be responsible for | |
bootstrapping the app data (if the user&#x27;s authentication token is already in | |
localStorage then we can simply retrieve the user&#x27;s data using that token). Then | |
the <code>&lt;UserProvider /&gt;</code> would be responsible for keeping the user data up to date | |
in memory and on the server as we make changes to the user&#x27;s data (like their | |
email address/bio/etc.).</p><p><a href="https://github.com/kentcdodds/bookshelf/blob/69bde2c117660bd988ffbc60f387165d2f852c62/src/context/auth-context.js">The <code>auth-context.js</code> file</a> | |
has some stuff in it that&#x27;s outside the scope of this blog post/domain specific, | |
so I&#x27;m only going to show a slimmed down/modified version of it:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {FullPageSpinner} from &#x27;../components/lib&#x27; | |
const AuthContext = React.createContext() | |
function AuthProvider(props) { | |
// code for pre-loading the user&#x27;s information if we have their token in localStorage goes here | |
// 🚨 this is the important bit. | |
// Normally your provider components render the context provider with a value. | |
// But we post-pone rendering any of the children until after we&#x27;ve determined | |
// whether or not we have a user token and if we do, then we render a spinner | |
// while we go retrieve that user&#x27;s information. | |
if (weAreStillWaitingToGetTheUserData) { | |
return &lt;FullPageSpinner /&gt; | |
} | |
const login = () =&gt; {} // make a login request | |
const register = () =&gt; {} // register the user | |
const logout = () =&gt; {} // clear the token in localStorage and the user data | |
// note, I&#x27;m not bothering to optimize this `value` with React.useMemo here | |
// because this is the top-most component rendered in our app and it will very | |
// rarely re-render/cause a performance problem. | |
return ( | |
&lt;AuthContext.Provider value={{data, login, logout, register}} {...props} /&gt; | |
) | |
} | |
const useAuth = () =&gt; React.useContext(AuthContext) | |
export {AuthProvider, useAuth} | |
// the UserProvider in user-context.js is basically: | |
// const UserProvider = props =&gt; ( | |
// &lt;UserContext.Provider value={useAuth().data.user} {...props} /&gt; | |
// ) | |
// and the useUser hook is basically this: | |
// const useUser = () =&gt; React.useContext(UserContext) | |
</code></pre><p>The key idea that drastically simplifies authentication in your app is this:</p><blockquote><p>The component which has the user data prevents the rest of the app from being | |
rendered until the user data is retrieved or it&#x27;s determined that there is no | |
logged-in user</p></blockquote><p>It does this by simply returning a spinner instead of rendering the rest of the | |
app. It&#x27;s not rendering a router or anything at all really. Just a spinner until | |
we know whether we have a user token <em>and</em> attempt to get that user&#x27;s | |
information. Once that&#x27;s done, then we can continue with rendering the rest of | |
the app.</p><h2>Conclusion</h2><p>Many apps are different. If you&#x27;re doing server-side rendering then you probably | |
don&#x27;t need a spinner and you have the user&#x27;s information available to you by the | |
time you start rendering. Even in that situation, taking a branch higher up in | |
the tree of your app drastically simplifies the maintenance of your app.</p><p>I hope this is helpful to you. You can checkout | |
<a href="https://github.com/kentcdodds/bookshelf">the bookshelf repo</a> | |
(<a href="https://codesandbox.io/s/github/kentcdodds/bookshelf">or even edit it on codesandbox</a>) | |
for a more complete picture of what all this is like in a more realistic | |
scenario with all the pieces together.</p><h2>P.S.</h2><p>Several people have asked me: What if my app has lots of shared screens between | |
authenticated and unauthenticated users (like Twitter) rather than having very | |
different screens between authenticated and unauthenticated users (like Gmail)?</p><p>In that case then you&#x27;ll probably need to litter a bunch of <code>useUser()</code> hooks | |
all over the codebase. You might make it even easier with a | |
<code>useIsAuthenticated()</code> hook that simply returns a boolean if the user is logged | |
in. Either way, it&#x27;s pretty simple thanks to context + hooks :)</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/authentication-in-react-applications">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How to optimize your context value]]></title> | |
<description><![CDATA[NOTE: I want to add that the times it's important to optimize your context | |
value is when a certain combination of the following conditions are met: Your context value changes frequently Your context has many consumers You are bothering to use React…]]></description> | |
<link>https://kentcdodds.com/blog/how-to-optimize-your-context-value</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-to-optimize-your-context-value</guid> | |
<pubDate>Mon, 06 May 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<blockquote><p>NOTE: I want to add that the times it&#x27;s important to optimize your context | |
value is when a certain combination of the following conditions are met:</p></blockquote><ol><li>Your context value changes frequently</li><li>Your context has many consumers</li><li>You are bothering to use <code>React.memo</code> (because things are legit slow)</li><li>You&#x27;ve actually measured things and you know it&#x27;s slow and needs to be | |
optimized</li></ol><p>If that explains your situation, then read on (and don&#x27;t miss the alternative | |
solution which is honestly probably better anyway). In fact, the alternative is | |
definitely better and I&#x27;ve reworked the blog post to remove my original | |
recommendation and just show the alternative. If you want to see my original, | |
<a href="https://github.com/kentcdodds/kentcdodds.com/blob/319db97260078ea4c263e75166f05e2cea21ccd1/content/blog/how-to-optimize-your-context-value/index.md">read the original stuff here</a>.</p><blockquote><p>No seriously, if you&#x27;re going to do this stuff just because you <em>think</em> your | |
code <em>might</em> be slow, then don&#x27;t bother. I&#x27;m not joking. React is really fast | |
and adding complexity in the name of performance when performance is good | |
enough is just wasteful of your &quot;complexity budget&quot;</p></blockquote><p>The simplest solution to optimizing your context value involves using | |
<code>useReducer</code> for your state management and putting the <code>state</code> in one context | |
provider and the <code>dispatch</code> in another. Here&#x27;s that:</p><p><iframe src="https://codesandbox.io/embed/ynn88nx9x?view=editor" style="width:100%;height:500px;border-width:0px;border-radius:4px;overflow:hidden"></iframe></p><p>Not only do you not need to <code>useMemo</code> in this case, but you actually can avoid | |
re-rendering the components that just use the updater context:</p><p><img src="https://kentcdodds.com/split-contexts-4c34ec3ca8069d6d9bea6abc52759a35.gif" alt="clicking &quot;force render&quot; three times and &quot;Increment count&quot; twice"/></p><p>This is the same as with my original <code>useMemo</code> solution, except because the | |
<code>&lt;Counter /&gt;</code> component&#x27;s context isn&#x27;t getting updated, we avoid the re-render | |
of that component entirely which is cool.</p><p>I personally feel like this is more complicated of an API than is necessary for | |
most situations, so I wouldn&#x27;t bother optimizing most of my contexts. But if you | |
really have all the problems mentioned above, then consider doing this as a | |
simple way to side-step the issue.</p><p>Also, don&#x27;t miss | |
<a href="https://kentcdodds.com/blog/how-to-use-react-context-effectively">How to use React Context effectively</a>.</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-to-optimize-your-context-value">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How to use React Context effectively]]></title> | |
<description><![CDATA[In | |
Application State Management with React , | |
I talk about how using a mix of local state and React Context can help you | |
manage state well in any React application. I showed some examples and I want to | |
call out a few things about those examples and…]]></description> | |
<link>https://kentcdodds.com/blog/how-to-use-react-context-effectively</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-to-use-react-context-effectively</guid> | |
<pubDate>Mon, 29 Apr 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>In | |
<a href="https://kentcdodds.com/blog/application-state-management-with-react">Application State Management with React</a>, | |
I talk about how using a mix of local state and React Context can help you | |
manage state well in any React application. I showed some examples and I want to | |
call out a few things about those examples and how you can create React context | |
consumers effectively so you avoid some problems and improve the developer | |
experience and maintainability of the context objects you create for your | |
application and/or libraries.</p><blockquote><p>Note, please do read | |
<a href="https://kentcdodds.com/blog/application-state-management-with-react">Application State Management with React</a> | |
and follow the advice that you shouldn&#x27;t be reaching for context to solve | |
every state sharing problem that crosses your desk. But when you do need to | |
reach for context, hopefully this blog post will help you know how to do so | |
effectively. Also, remember that context does NOT have to be global to the | |
whole app, but can be applied to one part of your tree and you can (and | |
probably should) have multiple logically separated contexts in your app.</p></blockquote><p>First, let&#x27;s create a file at <code>src/count-context.js</code> and we&#x27;ll create our | |
context there:</p><pre><code class="language-jsx">// src/count-context.js | |
import React from &#x27;react&#x27; | |
const CountStateContext = React.createContext() | |
const CountDispatchContext = React.createContext() | |
</code></pre><p>First off, I don&#x27;t have an initial value for the <code>CountStateContext</code>. If I | |
wanted an initial value, I would call <code>React.createContext({count: 0})</code>. But I | |
don&#x27;t include a default value and that&#x27;s intentional. The <code>defaultValue</code> is only | |
useful in a situation like this:</p><pre><code class="language-jsx" metastring="{2}">function CountDisplay() { | |
const {count} = React.useContext(CountStateContext) | |
return &lt;div&gt;{count}&lt;/div&gt; | |
} | |
ReactDOM.render(&lt;CountDisplay /&gt;, document.getElementById(&#x27;⚛️&#x27;)) | |
</code></pre><p>Because we don&#x27;t have a default value for our <code>CountStateContext</code>, we&#x27;ll get an | |
error on the highlighted line where we&#x27;re destructing the return value of | |
<code>useContext</code>. This is because our default value is <code>undefined</code> and you cannot | |
destructure <code>undefined</code>.</p><p>None of us likes runtime errors, so your knee-jerk reaction may be to add a | |
default value to avoid the runtime error. However, what use would the context be | |
if it didn&#x27;t have an actual value? If it&#x27;s just using the default value that&#x27;s | |
been provided, then it can&#x27;t really do much good. 99% of the time that you&#x27;re | |
going to be creating and using context in your application, you want your | |
context consumers (those using <code>useContext</code>) to be rendered within a provider | |
which can provide a useful value.</p><blockquote><p>Note, there are situations where default values are useful, but most of the | |
time they&#x27;re not necessary or useful.</p></blockquote><p><a href="https://reactjs.org/docs/context.html#reactcreatecontext">The React docs</a> | |
suggest that providing a default value &quot;can be helpful in testing components in | |
isolation without wrapping them.&quot; While it&#x27;s true that it allows you to do this, | |
I disagree that it&#x27;s better than wrapping your components with the necessary | |
context. Remember that every time you do something in your test that you don&#x27;t | |
do in your application, you reduce the amount of confidence that test can give | |
you. <a href="https://kentcdodds.com/blog/the-merits-of-mocking">There are reasons to do this</a>, but that&#x27;s not | |
one of them.</p><blockquote><p>Note: If you&#x27;re using Flow or TypeScript, not providing a default value can be | |
really annoying for people who are using <code>React.useContext</code>, but I&#x27;ll show you | |
how to avoid that problem altogether below. Keep reading!</p></blockquote><p><strong>What&#x27;s this <code>CountDispatchContext</code> thing all about?</strong> I&#x27;ve been playing around | |
with context for a while, and talking with friends at Facebook who have been | |
playing around with it for longer and I can tell you that the simplest way to | |
avoid problems with context (especially when you start calling <code>dispatch</code> in | |
effects) is to split up the state and dispatch in context. Stay with me here!</p><blockquote><p>If you want to dive into this a bit more, then read | |
<a href="https://kentcdodds.com/blog/how-to-optimize-your-context-value">How to optimize your context value</a></p></blockquote><h2>The Custom Provider Component</h2><p>Ok, let&#x27;s continue. For this context module to be useful <em>at all</em> we need to use | |
the Provider and expose a component that provides a value. Our component will be | |
used like this:</p><pre><code class="language-jsx" metastring="{3,6}">function App() { | |
return ( | |
&lt;CountProvider&gt; | |
&lt;CountDisplay /&gt; | |
&lt;Counter /&gt; | |
&lt;/CountProvider&gt; | |
) | |
} | |
ReactDOM.render(&lt;App /&gt;, document.getElementById(&#x27;⚛️&#x27;)) | |
</code></pre><p>So let&#x27;s make a component that can be used like that:</p><pre><code class="language-jsx">// src/count-context.js | |
import React from &#x27;react&#x27; | |
const CountStateContext = React.createContext() | |
const CountDispatchContext = React.createContext() | |
function countReducer(state, action) { | |
switch (action.type) { | |
case &#x27;increment&#x27;: { | |
return {count: state.count + 1} | |
} | |
case &#x27;decrement&#x27;: { | |
return {count: state.count - 1} | |
} | |
default: { | |
throw new Error(`Unhandled action type: ${action.type}`) | |
} | |
} | |
} | |
function CountProvider({children}) { | |
const [state, dispatch] = React.useReducer(countReducer, {count: 0}) | |
return ( | |
&lt;CountStateContext.Provider value={state}&gt; | |
&lt;CountDispatchContext.Provider value={dispatch}&gt; | |
{children} | |
&lt;/CountDispatchContext.Provider&gt; | |
&lt;/CountStateContext.Provider&gt; | |
) | |
} | |
export {CountProvider} | |
</code></pre><blockquote><p>NOTE: this is a contrived example that I&#x27;m intentionally over-engineering to | |
show you what a more real-world scenario would be like. <strong>This does not mean | |
it has to be this complicated every time!</strong> Feel free to use <code>useState</code> if | |
that suites your scenario. In addition, some providers are going to be short | |
and simple like this, and others are going to be MUCH more involved with many | |
hooks.</p></blockquote><h2>The Custom Consumer Hook</h2><p>Most of the APIs for context usages I&#x27;ve seen in the wild look something like | |
this:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {SomethingContext} from &#x27;some-context-package&#x27; | |
function YourComponent() { | |
const something = React.useContext(SomethingContext) | |
} | |
</code></pre><p>But I think that&#x27;s a missed opportunity at providing a better user experience. | |
Instead, I think it should be like this:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {useSomething} from &#x27;some-context-package&#x27; | |
function YourComponent() { | |
const something = useSomething() | |
} | |
</code></pre><p>This has the benefit of you being able to do a few things which I&#x27;ll show you in | |
the implementation now:</p><pre><code class="language-jsx" metastring="{32-38,40-46,48}">// src/count-context.js | |
import React from &#x27;react&#x27; | |
const CountStateContext = React.createContext() | |
const CountDispatchContext = React.createContext() | |
function countReducer(state, action) { | |
switch (action.type) { | |
case &#x27;increment&#x27;: { | |
return {count: state.count + 1} | |
} | |
case &#x27;decrement&#x27;: { | |
return {count: state.count - 1} | |
} | |
default: { | |
throw new Error(`Unhandled action type: ${action.type}`) | |
} | |
} | |
} | |
function CountProvider({children}) { | |
const [state, dispatch] = React.useReducer(countReducer, {count: 0}) | |
return ( | |
&lt;CountStateContext.Provider value={state}&gt; | |
&lt;CountDispatchContext.Provider value={dispatch}&gt; | |
{children} | |
&lt;/CountDispatchContext.Provider&gt; | |
&lt;/CountStateContext.Provider&gt; | |
) | |
} | |
function useCountState() { | |
const context = React.useContext(CountStateContext) | |
if (context === undefined) { | |
throw new Error(&#x27;useCountState must be used within a CountProvider&#x27;) | |
} | |
return context | |
} | |
function useCountDispatch() { | |
const context = React.useContext(CountDispatchContext) | |
if (context === undefined) { | |
throw new Error(&#x27;useCountDispatch must be used within a CountProvider&#x27;) | |
} | |
return context | |
} | |
export {CountProvider, useCountState, useCountDispatch} | |
</code></pre><p>First, the <code>useCountState</code> and <code>useCountDispatch</code> custom hooks use | |
<code>React.useContext</code> to get the provided context value from the nearest | |
<code>CountProvider</code>. However, if there is no value, then we throw a helpful error | |
message indicating that the hook is not being called within a function component | |
that is rendered within a <code>CountProvider</code>. This is most certainly a mistake, so | |
providing the error message is valuable. <em><strong>#FailFast</strong></em></p><h2>The Custom Consumer Component</h2><p>If you&#x27;re able to use hooks at all, then skip this section. However if you need | |
to support React <code>&lt;</code> 16.8.0, or you think the Context needs to be consumed by | |
class components, then here&#x27;s how you could do something similar with the | |
render-prop based API for context consumers:</p><pre><code class="language-jsx">function CountConsumer({children}) { | |
return ( | |
&lt;CountContext.Consumer&gt; | |
{context =&gt; { | |
if (context === undefined) { | |
throw new Error(&#x27;CountConsumer must be used within a CountProvider&#x27;) | |
} | |
return children(context) | |
}} | |
&lt;/CountContext.Consumer&gt; | |
) | |
} | |
</code></pre><p>This is what I used to do before we had hooks and it worked well. I would not | |
recommend bothering with this if you can use hooks though. Hooks are much | |
better.</p><h2>TypeScript / Flow</h2><p>I promised I&#x27;d show you how to avoid issues with skipping the <code>defaultValue</code> | |
when using TypeScript or Flow. Guess what! By doing what I&#x27;m suggesting, you | |
avoid the problem by default! It&#x27;s actually not a problem at all. Check it out:</p><pre><code class="language-typescript" metastring="{9-12,40-44,48-52}">// src/count-context.tsx | |
import * as React from &#x27;react&#x27; | |
type Action = {type: &#x27;increment&#x27;} | {type: &#x27;decrement&#x27;} | |
type Dispatch = (action: Action) =&gt; void | |
type State = {count: number} | |
type CountProviderProps = {children: React.ReactNode} | |
const CountStateContext = React.createContext&lt;State | undefined&gt;(undefined) | |
const CountDispatchContext = React.createContext&lt;Dispatch | undefined&gt;( | |
undefined, | |
) | |
function countReducer(state: State, action: Action) { | |
switch (action.type) { | |
case &#x27;increment&#x27;: { | |
return {count: state.count + 1} | |
} | |
case &#x27;decrement&#x27;: { | |
return {count: state.count - 1} | |
} | |
default: { | |
throw new Error(`Unhandled action type: ${action.type}`) | |
} | |
} | |
} | |
function CountProvider({children}: CountProviderProps) { | |
const [state, dispatch] = React.useReducer(countReducer, {count: 0}) | |
return ( | |
&lt;CountStateContext.Provider value={state}&gt; | |
&lt;CountDispatchContext.Provider value={dispatch}&gt; | |
{children} | |
&lt;/CountDispatchContext.Provider&gt; | |
&lt;/CountStateContext.Provider&gt; | |
) | |
} | |
function useCountState() { | |
const context = React.useContext(CountStateContext) | |
if (context === undefined) { | |
throw new Error(&#x27;useCountState must be used within a CountProvider&#x27;) | |
} | |
return context | |
} | |
function useCountDispatch() { | |
const context = React.useContext(CountDispatchContext) | |
if (context === undefined) { | |
throw new Error(&#x27;useCountDispatch must be used within a CountProvider&#x27;) | |
} | |
return context | |
} | |
export {CountProvider, useCountState, useCountDispatch} | |
</code></pre><p>With that, anyone can use <code>useCountState</code> or <code>useCountDispatch</code> without having | |
to do any undefined-checks, because we&#x27;re doing it for them!</p><p><a href="https://codesandbox.io/s/bitter-night-i5mhj">Here&#x27;s a working codesandbox</a></p><h2>What about dispatch <code>type</code> typos?</h2><p>At this point, you reduxers are yelling: &quot;Hey, where are the action creators?!&quot; | |
If you want to implement action creators that is fine by me, but I never liked | |
action creators. I have always felt like they were an unnecessary abstraction. | |
Also, if you are using TypeScript or Flow and have your actions well typed, then | |
you should not need them. You can get autocomplete and inline type errors!</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/d99d4329b1d7ebd2e63bf76471f540ab/27aa4/auto-complete.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:25%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="dispatch type getting autocompleted" title="dispatch type getting autocompleted" src="https://kentcdodds.com/static/d99d4329b1d7ebd2e63bf76471f540ab/17fa4/auto-complete.png" srcSet="https://kentcdodds.com/static/d99d4329b1d7ebd2e63bf76471f540ab/f4a45/auto-complete.png 259w,https://kentcdodds.com/static/d99d4329b1d7ebd2e63bf76471f540ab/ef0f6/auto-complete.png 518w,https://kentcdodds.com/static/d99d4329b1d7ebd2e63bf76471f540ab/17fa4/auto-complete.png 1035w,https://kentcdodds.com/static/d99d4329b1d7ebd2e63bf76471f540ab/d6f0c/auto-complete.png 1553w,https://kentcdodds.com/static/d99d4329b1d7ebd2e63bf76471f540ab/27aa4/auto-complete.png 1680w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/0d45ecb53470729181863636bdafc2b9/05bc5/type-error.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:22.55813953488372%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="type error on a misspelled dispatch type" title="type error on a misspelled dispatch type" src="https://kentcdodds.com/static/0d45ecb53470729181863636bdafc2b9/17fa4/type-error.png" srcSet="https://kentcdodds.com/static/0d45ecb53470729181863636bdafc2b9/f4a45/type-error.png 259w,https://kentcdodds.com/static/0d45ecb53470729181863636bdafc2b9/ef0f6/type-error.png 518w,https://kentcdodds.com/static/0d45ecb53470729181863636bdafc2b9/17fa4/type-error.png 1035w,https://kentcdodds.com/static/0d45ecb53470729181863636bdafc2b9/d6f0c/type-error.png 1553w,https://kentcdodds.com/static/0d45ecb53470729181863636bdafc2b9/05bc5/type-error.png 1720w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>I really like passing <code>dispatch</code> this way and as a side benefit, <code>dispatch</code> is | |
stable for the lifetime of the component that created it, so you don&#x27;t need to | |
worry about passing it to <code>useEffect</code> dependencies lists (it makes no difference | |
whether it is included or not).</p><p>If you are not typing your JavaScript (you probably should consider it if you | |
have not), then the error we throw for missed action types is a failsafe. Also, | |
read on to the next section because this can help you too.</p><h2>What about async actions?</h2><p>This is a great question. What happens if you have a situation where you need to | |
make some asynchronous request and you need to dispatch multiple things over the | |
course of that request? Sure you could do it at the calling component, but | |
manually wiring all of that together for every component that needs to do | |
something like that would be pretty annoying.</p><p>What I suggest is you make a helper function within your context module which | |
accepts <code>dispatch</code> along with any other data you need, and make that helper be | |
responsible for dealing with all of that. Here&#x27;s an example from | |
<a href="https://kentcdodds.com/workshops/advanced-react-patterns">my Advanced React Patterns workshop</a>:</p><pre><code class="language-javascript">// user-context.js | |
async function updateUser(dispatch, user, updates) { | |
dispatch({type: &#x27;start update&#x27;, updates}) | |
try { | |
const updatedUser = await userClient.updateUser(user, updates) | |
dispatch({type: &#x27;finish update&#x27;, updatedUser}) | |
} catch (error) { | |
dispatch({type: &#x27;fail update&#x27;, error}) | |
} | |
} | |
export {UserProvider, useUserDispatch, useUserState, updateUser} | |
</code></pre><p>Then you can use that like this:</p><pre><code class="language-javascript">// user-profile.js | |
import {useUserState, useUserDispatch, updateUser} from &#x27;./user-context&#x27; | |
function UserSettings() { | |
const {user, status, error} = useUserState() | |
const userDispatch = useUserDispatch() | |
function handleSubmit(event) { | |
event.preventDefault() | |
updateUser(userDispatch, user, formState) | |
} | |
// more code... | |
} | |
</code></pre><p>I&#x27;m really happy with this pattern and if you&#x27;d like me to teach this at your | |
company <a href="https://kentcdodds.com/contact">let me know</a> (or | |
<a href="https://kentcdodds.com/workshops/advanced-react-patterns">add yourself to the waitlist</a> for the next | |
time I host the workshop)!</p><h2>The state and dispatch separation is annoying</h2><p>Some people find this annoying/overly verbose:</p><pre><code class="language-javascript">const state = useCountState() | |
const dispatch = useCountDispatch() | |
</code></pre><p>They say &quot;can&#x27;t we just do this?&quot;:</p><pre><code class="language-javascript">const [state, dispatch] = useCount() | |
</code></pre><p>Sure you can:</p><pre><code class="language-javascript">function useCount() { | |
return [useCountState(), useCountDispatch()] | |
} | |
</code></pre><h2>Conclusion</h2><p>So here&#x27;s the final version of the code:</p><pre><code class="language-jsx">// src/count-context.js | |
import React from &#x27;react&#x27; | |
const CountStateContext = React.createContext() | |
const CountDispatchContext = React.createContext() | |
function countReducer(state, action) { | |
switch (action.type) { | |
case &#x27;increment&#x27;: { | |
return {count: state.count + 1} | |
} | |
case &#x27;decrement&#x27;: { | |
return {count: state.count - 1} | |
} | |
default: { | |
throw new Error(`Unhandled action type: ${action.type}`) | |
} | |
} | |
} | |
function CountProvider({children}) { | |
const [state, dispatch] = React.useReducer(countReducer, {count: 0}) | |
return ( | |
&lt;CountStateContext.Provider value={state}&gt; | |
&lt;CountDispatchContext.Provider value={dispatch}&gt; | |
{children} | |
&lt;/CountDispatchContext.Provider&gt; | |
&lt;/CountStateContext.Provider&gt; | |
) | |
} | |
function useCountState() { | |
const context = React.useContext(CountStateContext) | |
if (context === undefined) { | |
throw new Error(&#x27;useCountState must be used within a CountProvider&#x27;) | |
} | |
return context | |
} | |
function useCountDispatch() { | |
const context = React.useContext(CountDispatchContext) | |
if (context === undefined) { | |
throw new Error(&#x27;useCountDispatch must be used within a CountProvider&#x27;) | |
} | |
return context | |
} | |
export {CountProvider, useCountState, useCountDispatch} | |
</code></pre><p><a href="https://codesandbox.io/s/react-codesandbox-je6cc">Here&#x27;s a working codesandbox</a>.</p><p>Note that I&#x27;m <em>NOT</em> exporting <code>CountContext</code>. This is intentional. I expose only | |
one way to provide the context value and only one way to consume it. This allows | |
me to ensure that people are using the context value the way it should be and it | |
allows me to provide useful utilities for my consumers.</p><p>I hope this is useful to you! Remember:</p><ol><li>You shouldn&#x27;t be reaching for context to solve every state sharing problem | |
that crosses your desk.</li><li>Context does NOT have to be global to the whole app, but can be applied to | |
one part of your tree</li><li>You can (and probably should) have multiple logically separated contexts in | |
your app.</li></ol><p>Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-to-use-react-context-effectively">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Application State Management with React]]></title> | |
<description><![CDATA[Managing state is arguably the hardest part of any application. It's why there | |
are so many state management libraries available and more coming around every | |
day (and even some built on top of others... There are hundreds of "easier | |
redux…]]></description> | |
<link>https://kentcdodds.com/blog/application-state-management-with-react</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/application-state-management-with-react</guid> | |
<pubDate>Mon, 22 Apr 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Managing state is arguably the hardest part of any application. It&#x27;s why there | |
are so many state management libraries available and more coming around every | |
day (and even some built on top of others... There are hundreds of &quot;easier | |
redux&quot; abstractions on npm). Despite the fact that state management is a hard | |
problem, I would suggest that one of the things that makes it so difficult is | |
that we often over-engineer our solution to the problem.</p><p>There&#x27;s one state management solution that I&#x27;ve personally tried to implement | |
for as long as I&#x27;ve been using React, and with the release of React hooks (and | |
massive improvements to React context) this method of state management has been | |
drastically simplified.</p><p>We often talk about React components as lego building blocks to build our | |
applications, and I think that when people hear this, they somehow think this | |
excludes the state aspect. The &quot;secret&quot; behind my personal solution to the state | |
management problem is to think of how your application&#x27;s state maps to the | |
application&#x27;s tree structure.</p><p>One of the reasons redux was so successful was the fact that react-redux solved | |
the <a href="https://kentcdodds.com/blog/prop-drilling">prop drilling</a> problem. The fact that you could share | |
data across different parts of your tree by simply passing your component into | |
some magical <code>connect</code> function was wonderful. Its use of reducers/action | |
creators/etc. is great too, but I&#x27;m convinced that the ubiquity of redux is | |
because it solved the prop drilling pain point for developers.</p><p>Unfortunately, this led to the reason that I only ever used redux on one | |
project: I consistently see developers putting <em>all</em> of their state into redux. | |
Not just global application state, but local state as well. This leads to a lot | |
of problems, not the least of which is that when you&#x27;re maintaining any state | |
interaction, it involves interacting with reducers, action creators/types, and | |
dispatch calls, which ultimately results in having to open many files and trace | |
through the code in your head to figure out what&#x27;s happening and what impact it | |
has on the rest of the codebase.</p><p>To be clear, this is fine for state that is truly global, but for simple state | |
(like whether a modal is open or form input value state) this is a big problem. | |
To make matters worse, it doesn&#x27;t scale very well. The larger your application | |
gets, the harder this problem becomes. Sure you can hook up different reducers | |
to manage different parts of your application, but the indirection of going | |
through all these action creators and reducers is not optimal.</p><p>Having all your application state in a single object can also lead to other | |
problems, even if you&#x27;re not using Redux. When a React <code>&lt;Context.Provider&gt;</code> gets | |
a new value, all the components that consume that value are updated and have to | |
render, even if it&#x27;s a function component that only cares about part of the | |
data. That might lead to potential performance issues. (React-Redux v6 also | |
tried to use this approach until they realized it wouldn&#x27;t work right with | |
hooks, which forced them to use a different approach with v7 to solve these | |
issues.) But my point is that you don&#x27;t have this problem if you have your state | |
more logically separated and located in the react tree closer to where it | |
matters.</p><hr/><p>Here&#x27;s the real kicker, if you&#x27;re building an application with React, you | |
already have a state management library installed in your application. You don&#x27;t | |
even need to <code>npm install</code> (or <code>yarn add</code>) it. It costs no extra bytes for your | |
users, it integrates with all React packages on npm, and it&#x27;s already well | |
documented by the React team. It&#x27;s React itself.</p><blockquote><p><strong>React is a state management library</strong></p></blockquote><p>When you build a React application, you&#x27;re assembling a bunch of components to | |
make a tree of components starting at your <code>&lt;App /&gt;</code> and ending at your | |
<code>&lt;input /&gt;</code>s, <code>&lt;div /&gt;</code>s and <code>&lt;button /&gt;</code>s. You don&#x27;t manage all of the | |
low-level composite components that your application renders in one central | |
location. Instead, you let each individual component manage that and it ends up | |
being a really effective way to build your UI. You can do this with your state | |
as well, and it&#x27;s very likely that you do today:</p><pre><code class="language-javascript" metastring="{2}">function Counter() { | |
const [count, setCount] = React.useState(0) | |
const increment = () =&gt; setCount(c =&gt; c + 1) | |
return &lt;button onClick={increment}&gt;{count}&lt;/button&gt; | |
} | |
function App() { | |
return &lt;Counter /&gt; | |
} | |
</code></pre><p><a href="https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2F01-simple-count.js&amp;moduleview=1"><img src="https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit React Codesandbox"/></a></p><p>Note that everything I&#x27;m talking about here works with class components as well. | |
Hooks just make things a bit easier (especially context which we&#x27;ll get into in | |
a minute).</p><pre><code class="language-javascript" metastring="{2}">class Counter extends React.Component { | |
state = {count: 0} | |
increment = () =&gt; this.setState(({count}) =&gt; ({count: count + 1})) | |
render() { | |
return &lt;button onClick={this.increment}&gt;{this.state.count}&lt;/button&gt; | |
} | |
} | |
</code></pre><p>&quot;Ok, Kent, sure having a single element of state managed in a single component | |
is easy, but what do you do when I need to share that state across components? | |
For example, what if I wanted to do this:&quot;</p><pre><code class="language-javascript" metastring="{3}">function CountDisplay() { | |
// where does `count` come from? | |
return &lt;div&gt;The current counter count is {count}&lt;/div&gt; | |
} | |
function App() { | |
return ( | |
&lt;div&gt; | |
&lt;CountDisplay /&gt; | |
&lt;Counter /&gt; | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>&quot;The <code>count</code> is managed inside <code>&lt;Counter /&gt;</code>, now I need a state management | |
library to access that <code>count</code> value from the <code>&lt;CountDisplay /&gt;</code> and update it | |
in <code>&lt;Counter /&gt;</code>!&quot;</p><p>The answer to this problem is as old as React itself (older?) and has been in | |
the docs for as long as I can remember: | |
<a href="https://reactjs.org/docs/lifting-state-up.html">Lifting State Up</a></p><p>&quot;Lifting State Up&quot; is legitimately the answer to the state management problem in | |
React and it&#x27;s a rock solid one. Here&#x27;s how you apply it to this situation:</p><pre><code class="language-javascript">function Counter({count, onIncrementClick}) { | |
return &lt;button onClick={onIncrementClick}&gt;{count}&lt;/button&gt; | |
} | |
function CountDisplay({count}) { | |
return &lt;div&gt;The current counter count is {count}&lt;/div&gt; | |
} | |
function App() { | |
const [count, setCount] = React.useState(0) | |
const increment = () =&gt; setCount(c =&gt; c + 1) | |
return ( | |
&lt;div&gt; | |
&lt;CountDisplay count={count} /&gt; | |
&lt;Counter count={count} onIncrementClick={increment} /&gt; | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p><a href="https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2F02-lift-state.js&amp;moduleview=1"><img src="https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit React Codesandbox"/></a></p><p>We&#x27;ve just changed who&#x27;s responsible for our state and it&#x27;s really | |
straightforward. And we could keep lifting state all the way to the top of our | |
app.</p><p>&quot;Sure Kent, ok, but what about the <a href="https://kentcdodds.com/blog/prop-drilling">prop drilling</a> | |
problem?&quot;</p><p>This is one problem that&#x27;s actually also had a &quot;solution&quot; for a long time, but | |
only recently was that solution &quot;official&quot; and &quot;blessed.&quot; As I said, many people | |
reached for <code>react-redux</code> because it solved this problem using the mechanism I&#x27;m | |
referring to without them having to be worried about the warning that was in the | |
React docs. But now that <code>context</code> is an officially supported part of the React | |
API, we can use this directly without any problem:</p><pre><code class="language-javascript">// src/count/count-context.js | |
import React from &#x27;react&#x27; | |
const CountContext = React.createContext() | |
function useCount() { | |
const context = React.useContext(CountContext) | |
if (!context) { | |
throw new Error(`useCount must be used within a CountProvider`) | |
} | |
return context | |
} | |
function CountProvider(props) { | |
const [count, setCount] = React.useState(0) | |
const value = React.useMemo(() =&gt; [count, setCount], [count]) | |
return &lt;CountContext.Provider value={value} {...props} /&gt; | |
} | |
export {CountProvider, useCount} | |
// src/count/page.js | |
import React from &#x27;react&#x27; | |
import {CountProvider, useCount} from &#x27;./count-context&#x27; | |
function Counter() { | |
const [count, setCount] = useCount() | |
const increment = () =&gt; setCount(c =&gt; c + 1) | |
return &lt;button onClick={increment}&gt;{count}&lt;/button&gt; | |
} | |
function CountDisplay() { | |
const [count] = useCount() | |
return &lt;div&gt;The current counter count is {count}&lt;/div&gt; | |
} | |
function CountPage() { | |
return ( | |
&lt;div&gt; | |
&lt;CountProvider&gt; | |
&lt;CountDisplay /&gt; | |
&lt;Counter /&gt; | |
&lt;/CountProvider&gt; | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p><a href="https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2F03-context.js&amp;moduleview=1"><img src="https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit React Codesandbox"/></a></p><blockquote><p>NOTE: That particular code example is VERY contrived and I would NOT recommend | |
you reach for context to solve this specific scenario. Please read | |
<a href="https://kentcdodds.com/blog/prop-drilling">Prop Drilling</a> to get a better sense for why prop | |
drilling isn&#x27;t necessarily a problem and is often desirable. Don&#x27;t reach for | |
context too soon!</p></blockquote><p>And what&#x27;s cool about this approach is that we could put all the logic for | |
common ways to update the state in our <code>useContext</code> hook (or directly in context | |
if you want I guess):</p><pre><code class="language-javascript">function useCount() { | |
const context = React.useContext(CountContext) | |
if (!context) { | |
throw new Error(`useCount must be used within a CountProvider`) | |
} | |
const [count, setCount] = context | |
const increment = () =&gt; setCount(c =&gt; c + 1) | |
return { | |
count, | |
setCount, | |
increment, | |
} | |
} | |
</code></pre><p><a href="https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2F04-context-with-logic.js&amp;moduleview=1"><img src="https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit React Codesandbox"/></a></p><p>And you could easily change this to <code>useReducer</code> rather than <code>useState</code> as well:</p><pre><code class="language-javascript">function countReducer(state, action) { | |
switch (action.type) { | |
case &#x27;INCREMENT&#x27;: { | |
return {count: state.count + 1} | |
} | |
default: { | |
throw new Error(`Unsupported action type: ${action.type}`) | |
} | |
} | |
} | |
function CountProvider(props) { | |
const [state, dispatch] = React.useReducer(countReducer, {count: 0}) | |
const value = React.useMemo(() =&gt; [state, dispatch], [state]) | |
return &lt;CountContext.Provider value={value} {...props} /&gt; | |
} | |
function useCount() { | |
const context = React.useContext(CountContext) | |
if (!context) { | |
throw new Error(`useCount must be used within a CountProvider`) | |
} | |
const [state, dispatch] = context | |
const increment = () =&gt; dispatch({type: &#x27;INCREMENT&#x27;}) | |
return { | |
state, | |
dispatch, | |
increment, | |
} | |
} | |
</code></pre><p><a href="https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2F05-context-with-reducer.js&amp;moduleview=1"><img src="https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit React Codesandbox"/></a></p><p>This gives you an immense amount of flexibility and reduces complexity by orders | |
of magnitude. Here are a few important things to remember when doing things this | |
way:</p><ol><li>Not everything in your application needs to be in a single state object. Keep | |
things logically separated (user settings does not necessarily have to be in | |
the same context as notifications). You will have multiple providers with | |
this approach.</li><li>Not all of your context needs to be globally accessible! <strong>Keep state as | |
close to where it&#x27;s needed as possible.</strong></li></ol><p>More on that second point. Your app tree could look something like this:</p><pre><code class="language-javascript">function App() { | |
return ( | |
&lt;ThemeProvider&gt; | |
&lt;AuthenticationProvider&gt; | |
&lt;Router&gt; | |
&lt;Home path=&quot;/&quot; /&gt; | |
&lt;About path=&quot;/about&quot; /&gt; | |
&lt;UserPage path=&quot;/:userId&quot; /&gt; | |
&lt;UserSettings path=&quot;/settings&quot; /&gt; | |
&lt;Notifications path=&quot;/notifications&quot; /&gt; | |
&lt;/Router&gt; | |
&lt;/AuthenticationProvider&gt; | |
&lt;/ThemeProvider&gt; | |
) | |
} | |
function Notifications() { | |
return ( | |
&lt;NotificationsProvider&gt; | |
&lt;NotificationsTab /&gt; | |
&lt;NotificationsTypeList /&gt; | |
&lt;NotificationsList /&gt; | |
&lt;/NotificationsProvider&gt; | |
) | |
} | |
function UserPage({username}) { | |
return ( | |
&lt;UserProvider username={username}&gt; | |
&lt;UserInfo /&gt; | |
&lt;UserNav /&gt; | |
&lt;UserActivity /&gt; | |
&lt;/UserProvider&gt; | |
) | |
} | |
function UserSettings() { | |
// this would be the associated hook for the AuthenticationProvider | |
const {user} = useAuthenticatedUser() | |
} | |
</code></pre><p>Notice that each page can have its own provider that has data necessary for the | |
components underneath it. Code splitting &quot;just works&quot; for this stuff as well. | |
How you get data <em>into</em> each provider is up to the hooks those providers use and | |
how you retrieve data in your application, but you know just where to start | |
looking to find out how that works (in the provider).</p><h2>Conclusion</h2><p>Again, this is something that you can do with class components (you don&#x27;t have | |
to use hooks). Hooks make this much easier, but you could implement this | |
philosophy with React 15 no problem. Keep state as local as possible and use | |
context only when prop drilling really becomes a problem. Doing things this way | |
will make it easier for you to maintain state interactions.</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/application-state-management-with-react">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How to know what to test]]></title> | |
<description><![CDATA[Knowing how to test is great and important. I've created a LOT of content that | |
teaches people the fundamentals of testing, how to configure tools, how to write | |
tests for specific scenarios, and so on. But knowing how to write tests is | |
only half the…]]></description> | |
<link>https://kentcdodds.com/blog/how-to-know-what-to-test</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-to-know-what-to-test</guid> | |
<pubDate>Sat, 13 Apr 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Knowing how to test is great and important. I&#x27;ve created a LOT of content that | |
teaches people the fundamentals of testing, how to configure tools, how to write | |
tests for specific scenarios, and so on. But knowing <em>how</em> to write tests is | |
only half the battle to achieve confidence in your application. Knowing <em>what</em> | |
to test is the other–very important–battle.</p><p>In my <a href="https://kentcdodds.com/workshops">workshop material</a> and on | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>, I do talk about how to | |
know what to test, but I get asked about this enough that I thought it would be | |
good to write a blog post about it. So here you go!</p><h2>Remembering why we test</h2><p><strong>We write tests to be confident that our application will work when the user | |
uses them.</strong> Some people write tests to enhance their workflow as well and | |
that&#x27;s great, but I&#x27;m ultimately interested in confidence. That being the case, | |
what we test should map directly to enhancing our confidence. Here&#x27;s the key | |
point I want you to consider when writing tests:</p><blockquote><p><strong>Think less about the code you are testing and more about the use cases that | |
code supports.</strong></p></blockquote><p>When you think about the code itself, it&#x27;s too easy and natural to start testing | |
implementation details | |
(<a href="https://kentcdodds.com/blog/testing-implementation-details">which is road to disaster</a>).</p><p>Thinking about use cases though gets us closer to writing tests the way the user | |
uses the application:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">The more your tests resemble the way your software is used, the more confidence they can give you.</p>— Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/977018512689455106">March 23, 2018</a></blockquote></p><h2>Code Coverage &lt; Use Case Coverage</h2><p>Code coverage is a metric that shows us what lines of our code is being run | |
during the tests. Let&#x27;s use this code as an example:</p><pre><code class="language-javascript">function arrayify(maybeArray) { | |
if (Array.isArray(maybeArray)) { | |
return maybeArray | |
} else if (!maybeArray) { | |
return [] | |
} else { | |
return [maybeArray] | |
} | |
} | |
</code></pre><p>Right now, we have no tests for this function, so our code coverage report will | |
indicate that we have <code>0%</code> coverage of this function. The code coverage report | |
in this case helps give us an idea that tests are needed, but it does NOT tell | |
us what&#x27;s important about this function, <strong>nor does it tell us the use cases | |
this function supports</strong> which is the most important consideration we keep in | |
mind as we write tests.</p><p>In fact, when considering an entire application and wondering what to test, the | |
coverage report does a very poor job of giving us insight into where we should | |
be spending most of our time.</p><p>So the coverage report helps us identify what code in our codebase is missing | |
tests. So when you look at a code coverage report and note the lines that are | |
missing tests, don&#x27;t think about the ifs/elses, loops, or lifecycles. Instead | |
ask yourself:</p><blockquote><p>What use cases are these lines of code supporting, and what tests can I add to | |
support those use cases?</p></blockquote><p>&quot;Use Case Coverage&quot; tells us how many of the use cases our tests support. | |
Unfortunately, there&#x27;s no such thing as an automated &quot;Use Case Coverage Report.&quot; | |
We have to make that up ourselves. But the code coverage report can sometimes | |
help us identify use cases that we&#x27;re not covering. Let&#x27;s try it out.</p><p>So if we read at the code and consider it for a minute, we can identify our | |
first use case to support: &quot;it returns an array if given an array.&quot; This use | |
case statement is actually a great title for our test.</p><pre><code class="language-javascript">test(&#x27;returns an array if given an array&#x27;, () =&gt; { | |
expect(arrayify([&#x27;Elephant&#x27;, &#x27;Giraffe&#x27;])).toEqual([&#x27;Elephant&#x27;, &#x27;Giraffe&#x27;]) | |
}) | |
</code></pre><p>And with that test in place, our coverage report looks something like this | |
(highlighted lines are covered):</p><pre><code class="language-javascript" metastring="{1-3}">function arrayify(maybeArray) { | |
if (Array.isArray(maybeArray)) { | |
return maybeArray | |
} else if (!maybeArray) { | |
return [] | |
} else { | |
return [maybeArray] | |
} | |
} | |
</code></pre><p>Now, we can look at the remaining lines and determine that there are two more | |
use cases that our tests don&#x27;t support yet:</p><ul><li>it returns an empty array if given a falsy value</li><li>it returns an array with the given argument if it&#x27;s not an array or falsy</li></ul><p>Let&#x27;s add tests for those use cases and see how it effects the code coverage.</p><pre><code class="language-javascript">test(&#x27;returns an empty array if given a falsy value&#x27;, () =&gt; { | |
expect(arrayify()).toEqual([]) | |
}) | |
</code></pre><pre><code class="language-javascript" metastring="{1-5}">function arrayify(maybeArray) { | |
if (Array.isArray(maybeArray)) { | |
return maybeArray | |
} else if (!maybeArray) { | |
return [] | |
} else { | |
return [maybeArray] | |
} | |
} | |
</code></pre><p>Nice, almost there!</p><pre><code class="language-javascript">test(`returns an array with the given argument if it&#x27;s not an array or falsy`, () =&gt; { | |
expect(arrayify(&#x27;Leopard&#x27;)).toEqual([&#x27;Leopard&#x27;]) | |
}) | |
</code></pre><pre><code class="language-javascript" metastring="{1-9}">function arrayify(maybeArray) { | |
if (Array.isArray(maybeArray)) { | |
return maybeArray | |
} else if (!maybeArray) { | |
return [] | |
} else { | |
return [maybeArray] | |
} | |
} | |
</code></pre><p>Cool! Now we can be confident that so long as we don&#x27;t need to change the use | |
cases of this function, our tests will continue to pass.</p><p>Code coverage is not a perfect metric, but it can be a useful tool in | |
identifying what parts of our codebase are missing &quot;use case coverage&quot;.</p><h2>Code coverage can hide use cases</h2><p>Sometimes, our code coverage report indicates 100% code coverage, but not 100% | |
use case coverage. This is why sometimes I try to think of all the use cases | |
before I even start writing the tests.</p><p>For example, let&#x27;s imagine that the <code>arrayify</code> function had been implemented | |
like this instead:</p><pre><code class="language-javascript">function arrayify(maybeArray) { | |
if (Array.isArray(maybeArray)) { | |
return maybeArray | |
} else { | |
return [maybeArray].filter(Boolean) | |
} | |
} | |
</code></pre><p>With that, we can get 100% coverage with the following two use cases:</p><ul><li>it returns an array if given an array</li><li>it returns an array with the given argument if it&#x27;s not an array</li></ul><p>But if we could look at a <em>use case</em> coverage report, it would indicate that | |
we&#x27;re missing this use case:</p><ul><li>it returns an empty array if given a falsy value</li></ul><p>This could be bad because now our tests aren&#x27;t giving us as much confidence that | |
our code will work when users use it like this: <code>arrayify()</code>. Right now, it&#x27;s | |
fine because even though we don&#x27;t have a test for it, our code supports that use | |
case. But the reason we have tests in place is to ensure that code continues to | |
support the use cases we intend it to support, even as things change.</p><p>So, as an example for how missing this test can go wrong, someone could come | |
around, see that <code>.filter(Boolean)</code> thing and think: &quot;Huh, that&#x27;s weird... I | |
wonder if we really need that.&quot; So they remove it, and our tests continue to | |
pass, but any code that relied on the falsy behavior is now broken.</p><p>Key takeaway: <strong>Test use cases, not code.</strong></p><h2>How does this apply to React?</h2><p>When writing code, remember that you already have two users that you need to | |
support: End users, and developer users. Again, if you think about the code | |
rather than the use cases, it becomes dangerously natural to start testing | |
implementation details. When you do that, your code now has a third user.</p><p>Here are a few aspects of React that people often think about testing which | |
results in implementation details tests. For all of these, rather than thinking | |
about the code, think about the observable effect that code has for the end user | |
and developer user, that&#x27;s your use case, test that.</p><ul><li>Lifecycle methods</li><li>Element event handlers</li><li>Internal Component State</li></ul><p>Conversely, here are things that you should be testing because they concern your | |
two users. Each of these could change the DOM, make HTTP requests, call a | |
callback prop, or perform any other number of <em>observable</em> side effects which | |
would be useful to test:</p><ul><li>User interactions (using | |
<a href="https://testing-library.com/docs/dom-testing-library/api-events"><code>fireEvent</code></a> | |
from react-testing-library): Is the end user able to interact with the | |
elements that the component renders?</li><li>Changing props (using | |
<a href="https://testing-library.com/docs/react-testing-library/api#rerender"><code>rerender</code></a> | |
from react-testing-library): What happens when the developer user re-renders | |
your component with new props?</li><li>Context changes (using | |
<a href="https://testing-library.com/docs/react-testing-library/api#rerender"><code>rerender</code></a> | |
from react-testing-library): What happens when the developer user changes | |
context resulting in your component re-rendering?</li><li>Subscription changes: What happens when an event emitter the component | |
subscribes to changes? (Like firebase, a redux store, a router, a media query, | |
or DOM-based subscriptions like online status)</li></ul><h2>How do I know where to start in an app?</h2><p>So we know how to think about what to test for individual components and even | |
pages of our app, but where do you start? It&#x27;s a bit overwhelming. Especially if | |
you&#x27;re just getting started with testing in a large app.</p><p>So here&#x27;s what you do, consider your app from the user&#x27;s point of view and ask:</p><blockquote><p>What part of this app would make me most upset if it were broken?</p></blockquote><p>Alternatively, and more generally:</p><blockquote><p>What would be the worst thing to break in this app?</p></blockquote><p>I&#x27;d suggest making a list of features that your application supports and | |
prioritize them based on this criteria. It&#x27;s a great exercise to do with your | |
team and manager. This meeting will have the side-effect of helping everyone in | |
the room understand the importance of testing and hopefully convince them that | |
it should receive some level of prioritization in all the other feature work you | |
need to do.</p><p>Once you have that prioritized list, then I suggest writing a single end to end | |
(E2E) test to cover the &quot;happy path&quot; that most of your users go through for the | |
particular use case. Often you can cover parts of several of the top features on | |
your list this way. This may take a little while to get set up, but it&#x27;ll give | |
you a HUGE bang for your buck.</p><p>The E2E tests aren&#x27;t going to give you 100% use case coverage (and you should | |
not even try), nor will they give you 100% code coverage (and you should not | |
even record that for E2E tests anyway), but it will give you a great starting | |
point and boost your confidence big time.</p><p>Once you have a few E2E tests in place, then you can start looking at writing | |
some integration tests for some of the edge cases that you are missing in your | |
E2E tests and unit tests for the more complex business logic that those features | |
are using. From here it just becomes a matter of adding tests over time. Just | |
don&#x27;t bother with targeting a 100% code coverage report, it&#x27;s not worth the | |
time.</p><blockquote><p>For more on establishing a culture of testing and reasonable code coverage | |
targets, I suggest watching <a href="https://twitter.com/aarondjents">Aaron Abramov&#x27;s</a> | |
talk at <a href="https://2018.assertjs.com">AssertJS 2018</a>: | |
<a href="https://youtu.be/_pnW-JjmyXE?list=PLZ66c9_z3umNSrKSb5cmpxdXZcIPNvKGw">Establishing testing patterns with software design principles</a></p></blockquote><blockquote><p>Read more about the distinction between the different types of tests here: | |
<a href="https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests">Static vs Unit vs Integration vs E2E Testing for Frontend Apps</a></p></blockquote><h2>Conclusion</h2><p>Given enough time and experience, you develop an intuition for knowing what to | |
test. You&#x27;ll probably make mistakes and struggle a bit. Don&#x27;t give up! Keep | |
going. Good luck.</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-to-know-what-to-test">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[AHA Testing 💡]]></title> | |
<description><![CDATA[The AHA Programming Principle stands for "Avoid Hasty | |
Abstraction." I have specific feelings about how this applies to writing | |
maintainable tests. Most of the tests that I've seen in the wild have been | |
wildly on one side of the spectrum of…]]></description> | |
<link>https://kentcdodds.com/blog/aha-testing</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/aha-testing</guid> | |
<pubDate>Sun, 07 Apr 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>The <a href="https://kentcdodds.com/blog/aha-programming">AHA Programming Principle</a> stands for &quot;Avoid Hasty | |
Abstraction.&quot; I have specific feelings about how this applies to writing | |
maintainable tests. Most of the tests that I&#x27;ve seen in the wild have been | |
wildly on one side of the spectrum of abstraction: ANA (Absolutely No | |
Abstraction), or completely DRY (Don&#x27;t Repeat Yourself). (I made up ANA just | |
now).</p><style data-emotion-css="22jbx">.css-22jbx{width:100%;margin-top:10px;margin-bottom:40px;}</style><div class="css-22jbx"><style data-emotion-css="se5o5n">.css-se5o5n{width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;font-size:1.5em;text-align:center;line-height:1.2;margin-bottom:4px;}.css-se5o5n small,.css-se5o5n strong{display:block;}.css-se5o5n small{font-size:0.5em;}</style><div class="css-se5o5n"><style data-emotion-css="1u45wx3">.css-1u45wx3{color:rgb(94,49,220);}</style><div class="css-1u45wx3"><strong>ANA</strong><small>(Absolutely No Abstraction)</small></div><style data-emotion-css="1q25q80">.css-1q25q80{color:rgb(71,67,220);}</style><div class="css-1q25q80"><strong>AHA</strong><small>(Avoid Hasty Abstraction)</small></div><style data-emotion-css="dshstu">.css-dshstu{color:rgb(49,85,220);}</style><div class="css-dshstu"><strong>DRY</strong><small>(Don&#x27;t Repeat Yourself)</small></div></div><style data-emotion-css="r191zs">.css-r191zs{width:100%;height:20px;background-image:linear-gradient(-213deg,rgb(94,49,220) 0%,rgb(49,85,220) 100%);}</style><div class="css-r191zs"></div><style data-emotion-css="1u3gm1r">.css-1u3gm1r{margin-top:10px;color:rgb(71,67,220);background:linear-gradient(-213deg,rgb(94,49,220) 0%,rgb(49,85,220) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;font-size:1.8em;}@media (max-width:767px){.css-1u3gm1r{font-size:1em;}}</style><div class="css-1u3gm1r"><strong>T</strong><strong>h</strong><strong>e</strong><strong></strong><strong>S</strong><strong>p</strong><strong>e</strong><strong>c</strong><strong>t</strong><strong>r</strong><strong>u</strong><strong>m</strong><strong></strong><strong>o</strong><strong>f</strong><strong></strong><strong>A</strong><strong>b</strong><strong>s</strong><strong>t</strong><strong>r</strong><strong>a</strong><strong>c</strong><strong>t</strong><strong>i</strong><strong>o</strong><strong>n</strong></div></div><p>Finding a sweet spot in the middle of the spectrum of abstraction is key to | |
developing maintainable tests.</p><h2>ANA Testing</h2><p>The best example of &quot;Absolutely No Abstraction&quot; I&#x27;ve seen in testing is for | |
<a href="https://expressjs.com/en/guide/routing.html">ExpressJS route handlers</a>. For you | |
to understand what I mean when I say &quot;ANA is bad for testing&quot; I&#x27;m going to give | |
you a typical test file and ask you to pretend you&#x27;re going to maintain this | |
codebase and these tests. It&#x27;s important for you to understand how this route | |
works. You&#x27;re relieved that there are tests in place which will help you make | |
sure you&#x27;re not going to break something. So now you&#x27;re going to use the tests | |
to understand the nuances of the route handler.</p><p>Try to read this test and understand the one nuance between the two of them.</p><blockquote><p>Don&#x27;t spend too long on this...</p></blockquote><pre><code class="language-javascript">import * as blogPostController from &#x27;../blog-post&#x27; | |
// load the application-wide mock for the database. | |
// I guess that means this is AANA (Almost Absolutely No Abstraction) | |
// but I didn&#x27;t want to write out a whole db mock for this blog post 😅 | |
jest.mock(&#x27;../../lib/db&#x27;) | |
test(&#x27;lists blog posts for the logged in user&#x27;, async () =&gt; { | |
const req = { | |
locale: { | |
source: &#x27;default&#x27;, | |
language: &#x27;en&#x27;, | |
region: &#x27;GB&#x27;, | |
}, | |
user: { | |
guid: &#x27;0336397b-e29d-4b63-b94d-7e68a6fa3747&#x27;, | |
isActive: false, | |
picture: &#x27;http://placehold.it/32x32&#x27;, | |
age: 30, | |
name: { | |
first: &#x27;Francine&#x27;, | |
last: &#x27;Oconnor&#x27;, | |
}, | |
company: &#x27;ACME&#x27;, | |
email: &#x27;[email protected]&#x27;, | |
latitude: 51.507351, | |
longitude: -0.127758, | |
favoriteFruit: &#x27;banana&#x27;, | |
}, | |
body: {}, | |
cookies: {}, | |
query: {}, | |
params: { | |
bucket: &#x27;photography&#x27;, | |
}, | |
header(name) { | |
return { | |
Authorization: &#x27;Bearer TEST_TOKEN&#x27;, | |
}[name] | |
}, | |
} | |
const res = { | |
clearCookie: jest.fn(), | |
cookie: jest.fn(), | |
end: jest.fn(), | |
locals: { | |
content: {}, | |
}, | |
json: jest.fn(), | |
send: jest.fn(), | |
sendStatus: jest.fn(), | |
set: jest.fn(), | |
} | |
const next = jest.fn() | |
await blogPostController.loadBlogPosts(req, res, next) | |
expect(res.json).toHaveBeenCalledTimes(1) | |
expect(res.json).toHaveBeenCalledWith({ | |
posts: expect.arrayContaining([ | |
expect.objectContaining({ | |
title: &#x27;Test Post 1&#x27;, | |
subtitle: &#x27;This is the subtitle of Test Post 1&#x27;, | |
body: &#x27;The is the body of Test Post 1&#x27;, | |
}), | |
]), | |
}) | |
}) | |
test(&#x27;returns an empty list when there are no blog posts&#x27;, async () =&gt; { | |
const req = { | |
locale: { | |
source: &#x27;default&#x27;, | |
language: &#x27;en&#x27;, | |
region: &#x27;GB&#x27;, | |
}, | |
user: { | |
guid: &#x27;0336397b-e29d-4b63-b94d-7e68a6fa3747&#x27;, | |
isActive: false, | |
picture: &#x27;http://placehold.it/32x32&#x27;, | |
age: 30, | |
name: { | |
first: &#x27;Francine&#x27;, | |
last: &#x27;Oconnor&#x27;, | |
}, | |
company: &#x27;ACME&#x27;, | |
email: &#x27;[email protected]&#x27;, | |
latitude: 31.230416, | |
longitude: 121.473701, | |
favoriteFruit: &#x27;banana&#x27;, | |
}, | |
body: {}, | |
cookies: {}, | |
query: {}, | |
params: { | |
bucket: &#x27;photography&#x27;, | |
}, | |
header(name) { | |
return { | |
Authorization: &#x27;Bearer TEST_TOKEN&#x27;, | |
}[name] | |
}, | |
} | |
const res = { | |
clearCookie: jest.fn(), | |
cookie: jest.fn(), | |
end: jest.fn(), | |
locals: { | |
content: {}, | |
}, | |
json: jest.fn(), | |
send: jest.fn(), | |
sendStatus: jest.fn(), | |
set: jest.fn(), | |
} | |
const next = jest.fn() | |
await blogPostController.loadBlogPosts(req, res, next) | |
expect(res.json).toHaveBeenCalledTimes(1) | |
expect(res.json).toHaveBeenCalledWith({ | |
posts: [], | |
}) | |
}) | |
</code></pre><p>Did you find the difference? Yeah! We expect to find a post in the first one and | |
not in the second one! Cool! Great job. But... what causes that? Why does | |
<code>blogPostController.loadBlogPosts(req, res, next)</code> call <code>res.json</code> with a blog | |
post in the first one and not in the second one?</p><p>If you didn&#x27;t figure that out, don&#x27;t feel bad and don&#x27;t worry, I&#x27;ll show you | |
later. If you did, you&#x27;re probably really good at | |
<a href="https://en.wikipedia.org/wiki/Where%27s_Wally%3F">&quot;Where&#x27;s Wally&quot;</a> and that&#x27;s | |
my point. Tests like this make it harder than it needs to be to understand and | |
maintain the tests.</p><p>Now imagine that there are twenty such tests in a single file. You think it&#x27;s | |
terrible? Yes, it&#x27;s pretty bad. Never seen tests like this before? You&#x27;re lucky! | |
I&#x27;ve seen it a lot. Here&#x27;s how it gets this way:</p><ol><li>Engineer Joe joins a team</li><li>Joe needs to add a test</li><li>Joe copies a previous test that looks like what they need and modifies it for | |
their use case.</li><li>Reviewers observe that the tests pass and assume Joe knows what they&#x27;re | |
talking about.</li><li>PR is merged.</li></ol><p>Here&#x27;s your litmus test:</p><blockquote><p>How easy is it to determine the difference between assertions of two similar | |
tests and what causes that difference?</p></blockquote><p>Absolutely No Abstraction testing makes this very difficult.</p><h2>DRY Testing</h2><p>I don&#x27;t have time at the moment to give you a good example of a <code>DRY</code> test. Just | |
know that often what happens when people apply <code>DRY</code> to anything they typically | |
wind up being harder to maintain due to this process:</p><ol><li>Engineer Joe joins a team</li><li>Joe needs to add a test</li><li>Joes copies a previous test that looks basically exactly like what they need | |
and adds another <code>if</code> statement to the testing utility for their case.</li><li>Reviewers observe that the tests pass and assume Joe knows what they&#x27;re | |
talking about.</li><li>PR is merged.</li></ol><p>Another thing that I see a lot in DRY testing is the overuse of <code>describe</code> and | |
<code>it</code> nesting + <code>beforeEach</code>. The more you nest and use shared variables between | |
tests, the harder it is to follow the logic. I write about this problem a little | |
bit in <a href="https://kentcdodds.com/blog/test-isolation-with-react">Test Isolation with React</a> which I | |
recommend you read.</p><h2>AHA Testing</h2><p>That first test is absolutely screaming for abstraction (which is the guiding | |
principle for AHA programming). So let&#x27;s write a thoughtful abstraction for that | |
test. Now try to figure out what makes the difference in these tests:</p><pre><code class="language-javascript">import * as blogPostController from &#x27;../blog-post&#x27; | |
// load the application-wide mock for the database. | |
jest.mock(&#x27;../../lib/db&#x27;) | |
function setup(overrides = {}) { | |
const req = { | |
locale: { | |
source: &#x27;default&#x27;, | |
language: &#x27;en&#x27;, | |
region: &#x27;GB&#x27;, | |
}, | |
user: { | |
guid: &#x27;0336397b-e29d-4b63-b94d-7e68a6fa3747&#x27;, | |
isActive: false, | |
picture: &#x27;http://placehold.it/32x32&#x27;, | |
age: 30, | |
name: { | |
first: &#x27;Francine&#x27;, | |
last: &#x27;Oconnor&#x27;, | |
}, | |
company: &#x27;ACME&#x27;, | |
email: &#x27;[email protected]&#x27;, | |
latitude: 51.507351, | |
longitude: -0.127758, | |
favoriteFruit: &#x27;banana&#x27;, | |
}, | |
body: {}, | |
cookies: {}, | |
query: {}, | |
params: { | |
bucket: &#x27;photography&#x27;, | |
}, | |
header(name) { | |
return { | |
Authorization: &#x27;Bearer TEST_TOKEN&#x27;, | |
}[name] | |
}, | |
} | |
const res = { | |
clearCookie: jest.fn(), | |
cookie: jest.fn(), | |
end: jest.fn(), | |
locals: { | |
content: {}, | |
}, | |
json: jest.fn(), | |
send: jest.fn(), | |
sendStatus: jest.fn(), | |
set: jest.fn(), | |
} | |
const next = jest.fn() | |
return {req, res, next} | |
} | |
test(&#x27;lists blog posts for the logged in user&#x27;, async () =&gt; { | |
const {req, res, next} = setup() | |
await blogPostController.loadBlogPosts(req, res, next) | |
expect(res.json).toHaveBeenCalledTimes(1) | |
expect(res.json).toHaveBeenCalledWith({ | |
posts: expect.arrayContaining([ | |
expect.objectContaining({ | |
title: &#x27;Test Post 1&#x27;, | |
subtitle: &#x27;This is the subtitle of Test Post 1&#x27;, | |
body: &#x27;The is the body of Test Post 1&#x27;, | |
}), | |
]), | |
}) | |
}) | |
test(&#x27;returns an empty list when there are no blog posts&#x27;, async () =&gt; { | |
const {req, res, next} = setup() | |
req.user.latitude = 31.230416 | |
req.user.longitude = 121.473701 | |
await blogPostController.loadBlogPosts(req, res, next) | |
expect(res.json).toHaveBeenCalledTimes(1) | |
expect(res.json).toHaveBeenCalledWith({ | |
posts: [], | |
}) | |
}) | |
</code></pre><p>Now can you tell? What&#x27;s the difference between the first and the second test? | |
In the first our user is in London and in the second our user is in Shanghai! | |
Gee, sure would&#x27;ve been nice if our co-workers had told us we were working on a | |
location-based blogging platform (hey... now that&#x27;s an interesting product idea | |
🤔).</p><p>By adding just a little mindful abstraction, we&#x27;ve been able to make it much | |
more clear what actually matters in the difference of the inputs and outputs | |
leading to tests which make a LOT more sense and are WAY easier to maintain.</p><h2>AHA Testing with React</h2><p>In a react world, I will sometimes have a <code>renderFoo</code> function that acts like | |
the <code>setup</code> function here. Here&#x27;s a simple example:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {render, fireEvent} from &#x27;react-testing-library&#x27; | |
import LoginForm from &#x27;../login-form&#x27; | |
function renderLoginForm(props) { | |
const utils = render(&lt;LoginForm {...props} /&gt;) | |
const usernameInput = utils.getByLabelText(/username/i) | |
const passwordInput = utils.getByLabelText(/password/i) | |
const submitButton = utils.getByText(/submit/i) | |
return { | |
...utils, | |
usernameInput, | |
passwordInput, | |
submitButton, | |
changeUsername: value =&gt; fireEvent.change(usernameInput, {target: {value}}), | |
changePassword: value =&gt; fireEvent.change(passwordInput, {target: {value}}), | |
submitForm: () =&gt; fireEvent.click(submitButton), | |
} | |
} | |
test(&#x27;submit calls the submit handler&#x27;, () =&gt; { | |
const handleSubmit = jest.fn() | |
const {changeUsername, changePassword, submitForm} = renderLoginForm({ | |
onSubmit: handleSubmit, | |
}) | |
const username = &#x27;chucknorris&#x27; | |
const password = &#x27;ineednopassword&#x27; | |
changeUsername(username) | |
changePassword(password) | |
submitForm() | |
expect(handleSubmit).toHaveBeenCalledTimes(1) | |
expect(handleSubmit).toHaveBeenCalledWith({username, password}) | |
}) | |
</code></pre><blockquote><p>Note: I would consider this pre-mature abstraction if you&#x27;ve only got two or | |
three tests in the file that is using it and those tests are short. But if | |
you&#x27;ve got some nuance you&#x27;re testing (like error states for example), then | |
this kind of abstraction is great.</p></blockquote><h2>jest-in-case and test.each</h2><p>If you&#x27;re writing tests for a pure function, you&#x27;re in luck because those are | |
often the easiest to test for. You can seriously simplify your tests by using a | |
simple abstraction that calls out VERY clearly the outputs and inputs.</p><p>For (contrived) example:</p><pre><code class="language-javascript">import add from &#x27;../add&#x27; | |
test(&#x27;adds one and two to equal three&#x27;, () =&gt; { | |
expect(add(1, 2)).toBe(3) | |
}) | |
test(&#x27;adds three and four to equal seven&#x27;, () =&gt; { | |
expect(add(3, 4)).toBe(7) | |
}) | |
test(&#x27;adds one hundred and two to equal one hundred two&#x27;, () =&gt; { | |
expect(add(100, 2)).toBe(102) | |
}) | |
</code></pre><p>That&#x27;s pretty simple to follow, but it can be improved with <code>jest-in-case</code>:</p><pre><code class="language-javascript">import cases from &#x27;jest-in-case&#x27; | |
import add from &#x27;../add&#x27; | |
cases( | |
&#x27;add&#x27;, | |
({first, second, result}) =&gt; { | |
expect(add(first, second)).toBe(result) | |
}, | |
[ | |
{first: 1, second: 2, result: 3}, | |
{first: 3, second: 4, result: 7}, | |
{first: 100, second: 2, result: 102}, | |
], | |
) | |
</code></pre><p>I probably wouldn&#x27;t bother doing this for this simple example, but what&#x27;s cool | |
about it is that you can add more test cases very easily by simply adding more | |
elements to that array. A good example of this concept (that actually <em>doesn&#x27;t</em> | |
use jest-in-case) is | |
<a href="https://github.com/kentcdodds/rtl-css-js/blob/b148865ce6a4c994eba292015b8f44b5dae7edaa/src/__tests__/index.js">the <code>rtl-css-js</code> tests</a>. | |
Contributors to this codebase find it very easy to add new test cases with this | |
structure.</p><p>This can also be applied to impure functions and modules as well, though it | |
takes a little bit more work. | |
(<a href="https://github.com/kentcdodds/kcd-scripts/blob/7bc29e41e46e73b4b57c0f975648a90a75c24c80/src/scripts/__tests__/lint.js">Here&#x27;s a test that does this which I&#x27;m not totally proud of, but it&#x27;s not too bad</a>)</p><p>I personally prefer <a href="https://github.com/atlassian/jest-in-case">jest-in-case</a> | |
but Jest has a built-in | |
<a href="https://jestjs.io/docs/en/api#testeachtable-name-fn-timeout"><code>test.each</code></a> | |
functionality that you may find useful.</p><h2>Conclusion</h2><p>Certainly our tests could&#x27;ve been improved by providing better names and/or | |
comments as well, but our simple <code>setup</code> abstraction (by the way, that&#x27;s called | |
a &quot;Test Object Factory&quot;) doesn&#x27;t really need them. So my point is: <strong>it takes | |
less work to write and maintain tests that have mindful abstractions applied to | |
them.</strong></p><p>I hope that&#x27;s helpful! Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/aha-testing">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[AHA Programming 💡]]></title> | |
<description><![CDATA[DRY DRY (an acronym for "Don't Repeat Yourself") , | |
is an old software principle that Wikipedia sums up like this: Every piece of knowledge must have a single, unambiguous, authoritative | |
representation within a system This is generally a good practice…]]></description> | |
<link>https://kentcdodds.com/blog/aha-programming</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/aha-programming</guid> | |
<pubDate>Sun, 31 Mar 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<h2>DRY</h2><p><a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY (an acronym for &quot;Don&#x27;t Repeat Yourself&quot;)</a>, | |
is an old software principle that Wikipedia sums up like this:</p><blockquote><p>Every piece of knowledge must have a single, unambiguous, authoritative | |
representation within a system</p></blockquote><p>This is generally a good practice that I typically subscribe to (though less | |
dogmatically than that definition seems to encourage). The biggest problem I&#x27;ve | |
had with <a href="https://en.wikipedia.org/wiki/Duplicate_code">code duplication</a> (aka | |
copy/paste, it&#x27;s basically the antithesis of <code>DRY</code>) is discovering a bug in one | |
place, fixing it, then realizing that same bug was elsewhere and having to fix | |
it there as well.</p><p>Once, I inherited a codebase that made very heavy use of code duplication and | |
one time I had to fix a bug in eight different places! 😱 Talk about irritating! | |
Abstracting that code into a function that could be called anywhere it was | |
needed would&#x27;ve helped out a LOT.</p><h2>WET</h2><p>There&#x27;s another concept that people have referred to as <code>WET</code> programming which | |
stands for &quot;Write Everything Twice.&quot; That&#x27;s similarly dogmatic and over | |
prescriptive. <a href="https://twitter.com/CallMeWuz">Conlin Durbin</a> | |
<a href="https://dev.to/wuz/stop-trying-to-be-so-dry-instead-write-everything-twice-wet-5g33">has defined this as</a>:</p><blockquote><p>You can ask yourself &quot;Haven&#x27;t I written this before?&quot; two times, but never | |
three.</p></blockquote><p>In that same codebase I mentioned above, there was some over-abstraction that | |
was even more harmful than duplication. It was AngularJS code and for several | |
AngularJS controllers, the code passed <code>this</code> to a function which would | |
monkey-patch methods and properties onto <code>this</code> in a way enhancing the | |
controller instance with certain abilities. A sort of pseudo-inheritance thing I | |
guess. It was SUPER confusing, hard to follow, and I was terrified to make any | |
changes to that area of the codebase.</p><p>The code <em>was</em> reused in lots more than three places, but the abstraction was | |
bad and I wished that the code had been duplicated instead.</p><h2>AHA 💡</h2><p><code>AHA</code> (pronounced &quot;Aha!&quot; like you just made a discovery) is an acronym I | |
<a href="https://twitter.com/codehitchhiker/status/1112819136147742720">got from</a> | |
<a href="https://twitter.com/codehitchhiker">Cher</a> which stands for</p><blockquote><p>Avoid Hasty Abstractions</p></blockquote><p>The way I think of this principle is beautifully described by | |
<a href="https://twitter.com/sandimetz">Sandi Metz</a> who | |
<a href="https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction">wrote</a>:</p><blockquote><p>prefer duplication over the wrong abstraction</p></blockquote><p>This is such a solid golden nugget of wisdom that I want you to read it again, | |
then read Sandi&#x27;s blog post on the subject: | |
<a href="https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction">The Wrong Abstraction</a>. | |
It&#x27;s fantastic.</p><p>Here&#x27;s another important related principle that I want to add:</p><blockquote><p>Optimize for change first</p></blockquote><p>I think the key is that we don&#x27;t know what the future of code will be. We could | |
spend weeks optimizing code for performance, or coming up with the best API for | |
our new abstraction, only to find out the next day that we made incorrect | |
assumptions and the API needs a complete rework or the feature the code was | |
written for is no longer needed. We don&#x27;t know for sure. All we can really be | |
sure of is that things will probably change, and if they never do then we wont | |
touch the code anyway so who cares what it looks like?</p><p>Now, don&#x27;t get me wrong, I&#x27;m not suggesting anarchy. I&#x27;m just suggesting that we | |
should be mindful of the fact that we don&#x27;t <em>really</em> know what requirements will | |
be placed upon our code in the future.</p><p>So I&#x27;m fine with code duplication until you feel pretty confident that you know | |
the use cases for that duplicate code. What parts of the code are different that | |
would make good arguments to your function? After you&#x27;ve got a few places where | |
that code is running, the commonalities will scream at you for abstraction and | |
you&#x27;ll be in the right frame of mind to provide that abstraction.</p><p>If you abstract early though, you&#x27;ll think the function or component is perfect | |
for your use case and so you just bend the code to fit your new use case. This | |
goes on several times until the abstraction is basically your whole application | |
in <code>if</code> statements and loops 😂😭</p><p><strong>I think the big takeaway</strong> about &quot;AHA Programming&quot; is that you shouldn&#x27;t be | |
dogmatic about when you start writing abstractions but instead write the | |
abstraction when it <em>feels</em> right and don&#x27;t be afraid to duplicate code until | |
you get there.</p><h2>Conclusion</h2><p>I feel like a measured and pragmatic approach to software principles is | |
important. That&#x27;s why I prefer <code>AHA</code> over <code>DRY</code> or <code>WET</code>. It&#x27;s intended to help | |
you be mindful of your abstractions without giving hard-fast rules to when it is | |
or isn&#x27;t ok to abstract some code into a function or module.</p><p>I hope that&#x27;s helpful to you. If you find yourself mired in bad abstractions, | |
take heart! Sandi gives you some good steps for how to get out of that! | |
<a href="https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction">Just read her blog post</a>. | |
Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/aha-programming">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[The State Reducer Pattern with React Hooks]]></title> | |
<description><![CDATA[Some History About a year ago, I developed a new pattern for enhancing your React components | |
called the state reducer pattern . I used it | |
in downshift to enable an awesome | |
API for people who wanted to make changes to how downshift updates state…]]></description> | |
<link>https://kentcdodds.com/blog/the-state-reducer-pattern-with-react-hooks</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/the-state-reducer-pattern-with-react-hooks</guid> | |
<pubDate>Mon, 25 Mar 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<h2>Some History</h2><p>About a year ago, I developed a new pattern for enhancing your React components | |
called <a href="https://kentcdodds.com/blog/the-state-reducer-pattern">the state reducer pattern</a>. I used it | |
in <a href="https://github.com/downshift-js/downshift">downshift</a> to enable an awesome | |
API for people who wanted to make changes to how downshift updates state | |
internally.</p><blockquote><p>If you&#x27;re unfamiliar with downshift, just know that it&#x27;s an &quot;enhanced input&quot; | |
component that allows you to build things like accessible | |
autocomplete/typeahead/dropdown components. It&#x27;s important to know that it | |
manages the following items of state: <code>isOpen</code>, <code>selectedItem</code>, | |
<code>highlightedIndex</code>, and <code>inputValue</code>.</p></blockquote><p>Downshift is currently implemented as a render prop component, because at the | |
time, render props was the best way to make a | |
<a href="https://www.merrickchristensen.com/articles/headless-user-interface-components">&quot;Headless UI Component&quot;</a> | |
(typically implemented via a &quot;render prop&quot; API) which made it possible for you | |
to share logic without being opinionated about the UI. This is the major reason | |
that downshift is so successful.</p><p>Today however, we have <a href="https://reactjs.org/hooks">React Hooks</a> and | |
<a href="https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-render-props">hooks are way better at doing this than render props</a>. | |
So I thought I&#x27;d give you all an update of how this pattern transfers over to | |
this new API the React team has given us. (Note: | |
<a href="https://github.com/downshift-js/downshift/issues/683">Downshift has plans to implement a hook</a>)</p><p>As a reminder, the benefit of the state reducer pattern is in the fact that it | |
allows | |
<a href="https://en.wikipedia.org/wiki/Inversion_of_control">&quot;inversion of control&quot;</a> | |
which is basically a mechanism for the author of the API to allow the user of | |
the API to control how things work internally. For an example-based talk about | |
this, I strongly recommend you give my React Rally 2018 talk a watch:</p><p><iframe width="100%" height="315" src="https://www.youtube-nocookie.com/embed/AiJ8tRRH0f8?rel=0&amp;list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></p><p>So in the downshift example, I had made the decision that when an end user | |
selects an item, the <code>isOpen</code> should be set to <code>false</code> (and the menu should be | |
closed). Someone was building a multi-select with downshift and wanted to keep | |
the menu open after the user selects an item in the menu (so they can continue | |
to select more).</p><p>By inverting control of state updates with the state reducer pattern, I was able | |
to enable their use case as well as any other use case people could possibly | |
want when they want to change how downshift operates internally. Inversion of | |
control is an enabling computer science principle and the state reducer pattern | |
is an awesome implementation of that idea that translates even better to hooks | |
than it did to regular components.</p><h2>Using a State Reducer with Hooks</h2><p>Ok, so the concept goes like this:</p><ol><li>End user does an action</li><li>Dev calls dispatch</li><li>Hook determines the necessary changes</li><li>Hook calls dev&#x27;s code for further changes 👈 this is the inversion of control | |
part</li><li>Hook makes the state changes</li></ol><p><strong>WARNING: Contrived example ahead</strong>: To keep things simple, I&#x27;m going to use a | |
simple <code>useToggle</code> hook and component as a starting point. It&#x27;ll feel contrived, | |
but I don&#x27;t want you to get distracted by a complicated example as I teach you | |
how to use this pattern with hooks. Just know that this pattern works best when | |
it&#x27;s applied to complex hooks and components (like downshift).</p><pre><code class="language-jsx">function useToggle() { | |
const [on, setOnState] = React.useState(false) | |
const toggle = () =&gt; setOnState(o =&gt; !o) | |
const setOn = () =&gt; setOnState(true) | |
const setOff = () =&gt; setOnState(false) | |
return {on, toggle, setOn, setOff} | |
} | |
function Toggle() { | |
const {on, toggle, setOn, setOff} = useToggle() | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={setOff}&gt;Switch Off&lt;/button&gt; | |
&lt;button onClick={setOn}&gt;Switch On&lt;/button&gt; | |
&lt;Switch on={on} onClick={toggle} /&gt; | |
&lt;/div&gt; | |
) | |
} | |
function App() { | |
return &lt;Toggle /&gt; | |
} | |
ReactDOM.render(&lt;App /&gt;, document.getElementById(&#x27;root&#x27;)) | |
</code></pre><p>Now, let&#x27;s say we wanted to adjust the <code>&lt;Toggle /&gt;</code> component so the user | |
couldn&#x27;t click the <code>&lt;Switch /&gt;</code> more than 4 times in a row unless they click a | |
&quot;Reset&quot; button:</p><pre><code class="language-jsx" metastring="{2-3,7-10,16-19}">function Toggle() { | |
const [clicksSinceReset, setClicksSinceReset] = React.useState(0) | |
const tooManyClicks = clicksSinceReset &gt;= 4 | |
const {on, toggle, setOn, setOff} = useToggle() | |
function handleClick() { | |
toggle() | |
setClicksSinceReset(count =&gt; count + 1) | |
} | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={setOff}&gt;Switch Off&lt;/button&gt; | |
&lt;button onClick={setOn}&gt;Switch On&lt;/button&gt; | |
&lt;Switch on={on} onClick={handleClick} /&gt; | |
{tooManyClicks ? ( | |
&lt;button onClick={() =&gt; setClicksSinceReset(0)}&gt;Reset&lt;/button&gt; | |
) : null} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>Cool, so an easy solution to this problem would be to add an if statement in the | |
<code>handleClick</code> function and not call <code>toggle</code> if <code>tooManyClicks</code> is true, but | |
let&#x27;s keep going for the purposes of this example.</p><p>How could we change the <code>useToggle</code> hook, to <em>invert control</em> in this situation? | |
Let&#x27;s think about the API first, then the implementation second. As a user, it&#x27;d | |
be cool if I could hook into every state update before it actually happens and | |
modify it, like so:</p><pre><code class="language-jsx" metastring="{6-14}">function Toggle() { | |
const [clicksSinceReset, setClicksSinceReset] = React.useState(0) | |
const tooManyClicks = clicksSinceReset &gt;= 4 | |
const {on, toggle, setOn, setOff} = useToggle({ | |
modifyStateChange(currentState, changes) { | |
if (tooManyClicks) { | |
// other changes are fine, but on needs to be unchanged | |
return {...changes, on: currentState.on} | |
} else { | |
// the changes are fine | |
return changes | |
} | |
}, | |
}) | |
function handleClick() { | |
toggle() | |
setClicksSinceReset(count =&gt; count + 1) | |
} | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={setOff}&gt;Switch Off&lt;/button&gt; | |
&lt;button onClick={setOn}&gt;Switch On&lt;/button&gt; | |
&lt;Switch on={on} onClick={handleClick} /&gt; | |
{tooManyClicks ? ( | |
&lt;button onClick={() =&gt; setClicksSinceReset(0)}&gt;Reset&lt;/button&gt; | |
) : null} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>So that&#x27;s great, except it prevents changes from happening when people click the | |
&quot;Switch Off&quot; or &quot;Switch On&quot; buttons, and we only want to prevent the | |
<code>&lt;Switch /&gt;</code> from toggling the state.</p><p>Hmmm... What if we change <code>modifyStateChange</code> to be called <code>reducer</code> and it | |
accepts an <code>action</code> as the second argument? Then the <code>action</code> could have a | |
<code>type</code> that determines what type of change is happening, and the <code>changes</code> could | |
just be a property on that object. We&#x27;ll just say that the <code>type</code> for clicking | |
the switch is <code>TOGGLE</code>.</p><pre><code class="language-jsx" metastring="{6-7}">function Toggle() { | |
const [clicksSinceReset, setClicksSinceReset] = React.useState(0) | |
const tooManyClicks = clicksSinceReset &gt;= 4 | |
const {on, toggle, setOn, setOff} = useToggle({ | |
reducer(currentState, action) { | |
if (tooManyClicks &amp;&amp; action.type === &#x27;TOGGLE&#x27;) { | |
// other changes are fine, but on needs to be unchanged | |
return {...action.changes, on: currentState.on} | |
} else { | |
// the changes are fine | |
return action.changes | |
} | |
}, | |
}) | |
function handleClick() { | |
toggle() | |
setClicksSinceReset(count =&gt; count + 1) | |
} | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={setOff}&gt;Switch Off&lt;/button&gt; | |
&lt;button onClick={setOn}&gt;Switch On&lt;/button&gt; | |
&lt;Switch on={on} onClick={handleClick} /&gt; | |
{tooManyClicks ? ( | |
&lt;button onClick={() =&gt; setClicksSinceReset(0)}&gt;Reset&lt;/button&gt; | |
) : null} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>Nice! This gives us all kinds of control. One last thing, let&#x27;s not bother with | |
the string <code>&#x27;TOGGLE&#x27;</code> for the type. Instead we&#x27;ll have an object of all the | |
change types that people can reference instead. This&#x27;ll help avoid typos and | |
improve editor autocompletion:</p><pre><code class="language-jsx" metastring="{7}">function Toggle() { | |
const [clicksSinceReset, setClicksSinceReset] = React.useState(0) | |
const tooManyClicks = clicksSinceReset &gt;= 4 | |
const {on, toggle, setOn, setOff} = useToggle({ | |
reducer(currentState, action) { | |
if (tooManyClicks &amp;&amp; action.type === useToggle.types.toggle) { | |
// other changes are fine, but on needs to be unchanged | |
return {...action.changes, on: currentState.on} | |
} else { | |
// the changes are fine | |
return action.changes | |
} | |
}, | |
}) | |
function handleClick() { | |
toggle() | |
setClicksSinceReset(count =&gt; count + 1) | |
} | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={setOff}&gt;Switch Off&lt;/button&gt; | |
&lt;button onClick={setOn}&gt;Switch On&lt;/button&gt; | |
&lt;Switch on={on} onClick={handleClick} /&gt; | |
{tooManyClicks ? ( | |
&lt;button onClick={() =&gt; setClicksSinceReset(0)}&gt;Reset&lt;/button&gt; | |
) : null} | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><h2>Implementing a State Reducer with Hooks</h2><p>Alright, I&#x27;m happy with the API we&#x27;re exposing here. Let&#x27;s take a look at how we | |
could implement this with our <code>useToggle</code> hook. In case you forgot, here&#x27;s the | |
code for that:</p><pre><code class="language-jsx">function useToggle() { | |
const [on, setOnState] = React.useState(false) | |
const toggle = () =&gt; setOnState(o =&gt; !o) | |
const setOn = () =&gt; setOnState(true) | |
const setOff = () =&gt; setOnState(false) | |
return {on, toggle, setOn, setOff} | |
} | |
</code></pre><p>We <em>could</em> add logic to every one of these helper functions, but I&#x27;m just going | |
to skip ahead and tell you that this would be really annoying, even in this | |
simple hook. Instead, we&#x27;re going to rewrite this from <code>useState</code> to | |
<code>useReducer</code> and that&#x27;ll make our implementation a LOT easier:</p><pre><code class="language-jsx">function toggleReducer(state, action) { | |
switch (action.type) { | |
case &#x27;TOGGLE&#x27;: { | |
return {on: !state.on} | |
} | |
case &#x27;ON&#x27;: { | |
return {on: true} | |
} | |
case &#x27;OFF&#x27;: { | |
return {on: false} | |
} | |
default: { | |
throw new Error(`Unhandled type: ${action.type}`) | |
} | |
} | |
} | |
function useToggle() { | |
const [{on}, dispatch] = React.useReducer(toggleReducer, {on: false}) | |
const toggle = () =&gt; dispatch({type: &#x27;TOGGLE&#x27;}) | |
const setOn = () =&gt; dispatch({type: &#x27;ON&#x27;}) | |
const setOff = () =&gt; dispatch({type: &#x27;OFF&#x27;}) | |
return {on, toggle, setOn, setOff} | |
} | |
</code></pre><p>Ok, cool. Really quick, let&#x27;s add that <code>types</code> property to our <code>useToggle</code> to | |
avoid the strings thing:</p><pre><code class="language-jsx" metastring="{3,6,9,21-23,28-32}">function toggleReducer(state, action) { | |
switch (action.type) { | |
case useToggle.types.toggle: { | |
return {on: !state.on} | |
} | |
case useToggle.types.on: { | |
return {on: true} | |
} | |
case useToggle.types.off: { | |
return {on: false} | |
} | |
default: { | |
throw new Error(`Unhandled type: ${action.type}`) | |
} | |
} | |
} | |
function useToggle() { | |
const [{on}, dispatch] = React.useReducer(toggleReducer, {on: false}) | |
const toggle = () =&gt; dispatch({type: useToggle.types.toggle}) | |
const setOn = () =&gt; dispatch({type: useToggle.types.on}) | |
const setOff = () =&gt; dispatch({type: useToggle.types.off}) | |
return {on, toggle, setOn, setOff} | |
} | |
useToggle.types = { | |
toggle: &#x27;TOGGLE&#x27;, | |
on: &#x27;ON&#x27;, | |
off: &#x27;OFF&#x27;, | |
} | |
</code></pre><p>Cool, so now, users are going to pass <code>reducer</code> as a configuration object to our | |
<code>useToggle</code> function, so let&#x27;s accept that:</p><pre><code class="language-jsx" metastring="{1}">function useToggle({reducer}) { | |
const [{on}, dispatch] = React.useReducer(toggleReducer, {on: false}) | |
const toggle = () =&gt; dispatch({type: useToggle.types.toggle}) | |
const setOn = () =&gt; dispatch({type: useToggle.types.on}) | |
const setOff = () =&gt; dispatch({type: useToggle.types.off}) | |
return {on, toggle, setOn, setOff} | |
} | |
</code></pre><p>Great, so now that we have the developer&#x27;s <code>reducer</code>, how do we combine that | |
with our reducer? Well remember that the developer needs to know what our | |
changes will be, so we&#x27;ll definitely need to determine those changes first. | |
Let&#x27;s make an inline reducer:</p><pre><code class="language-jsx" metastring="{3-6}">function useToggle({reducer}) { | |
const [{on}, dispatch] = React.useReducer( | |
(state, action) =&gt; { | |
const changes = toggleReducer(state, action) | |
return changes | |
}, | |
{on: false}, | |
) | |
const toggle = () =&gt; dispatch({type: useToggle.types.toggle}) | |
const setOn = () =&gt; dispatch({type: useToggle.types.on}) | |
const setOff = () =&gt; dispatch({type: useToggle.types.off}) | |
return {on, toggle, setOn, setOff} | |
} | |
</code></pre><p>That was a straight-up refactor. In fact, no functionality has changed for our | |
toggle hook (which is actually kinda neat if you think of it. The magic of black | |
boxes and implementation details ✨).</p><p>Awesome, so now we have all the info we need to pass along to the <code>reducer</code> | |
they&#x27;ve given to us:</p><pre><code class="language-jsx" metastring="{5}">function useToggle({reducer}) { | |
const [{on}, dispatch] = React.useReducer( | |
(state, action) =&gt; { | |
const changes = toggleReducer(state, action) | |
return reducer(state, {...action, changes}) | |
}, | |
{on: false}, | |
) | |
const toggle = () =&gt; dispatch({type: useToggle.types.toggle}) | |
const setOn = () =&gt; dispatch({type: useToggle.types.on}) | |
const setOff = () =&gt; dispatch({type: useToggle.types.off}) | |
return {on, toggle, setOn, setOff} | |
} | |
</code></pre><p>Cool! So we just call the developer&#x27;s <code>reducer</code> with the <code>state</code> and make a new | |
<code>action</code> object that has all the properties of the original <code>action</code> plus the | |
<code>changes</code>. Then we return whatever they return to us. And they have complete | |
control over our state updates! That&#x27;s pretty neat! And thanks to <code>useReducer</code> | |
it&#x27;s pretty simple too.</p><p>But not everyone&#x27;s going to need this <code>reducers</code> feature, so let&#x27;s default the | |
configuration object to <code>{}</code> and we&#x27;ll default the <code>reducer</code> property to a | |
simple reducer that just always returns the changes:</p><pre><code class="language-jsx" metastring="{1}">function useToggle({reducer = (s, a) =&gt; a.changes} = {}) { | |
const [{on}, dispatch] = React.useReducer( | |
(state, action) =&gt; { | |
const changes = toggleReducer(state, action) | |
return reducer(state, {...action, changes}) | |
}, | |
{on: false}, | |
) | |
const toggle = () =&gt; dispatch({type: useToggle.types.toggle}) | |
const setOn = () =&gt; dispatch({type: useToggle.types.on}) | |
const setOff = () =&gt; dispatch({type: useToggle.types.off}) | |
return {on, toggle, setOn, setOff} | |
} | |
</code></pre><h2>Conclusion</h2><p>Here&#x27;s the final version:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import ReactDOM from &#x27;react-dom&#x27; | |
import Switch from &#x27;./switch&#x27; | |
function toggleReducer(state, action) { | |
switch (action.type) { | |
case useToggle.types.toggle: { | |
return {on: !state.on} | |
} | |
case useToggle.types.on: { | |
return {on: true} | |
} | |
case useToggle.types.off: { | |
return {on: false} | |
} | |
default: { | |
throw new Error(`Unhandled type: ${action.type}`) | |
} | |
} | |
} | |
function useToggle({reducer = (s, a) =&gt; a.changes} = {}) { | |
const [{on}, dispatch] = React.useReducer( | |
(state, action) =&gt; { | |
const changes = toggleReducer(state, action) | |
return reducer(state, {...action, changes}) | |
}, | |
{on: false}, | |
) | |
const toggle = () =&gt; dispatch({type: useToggle.types.toggle}) | |
const setOn = () =&gt; dispatch({type: useToggle.types.on}) | |
const setOff = () =&gt; dispatch({type: useToggle.types.off}) | |
return {on, toggle, setOn, setOff} | |
} | |
useToggle.types = { | |
toggle: &#x27;TOGGLE&#x27;, | |
on: &#x27;ON&#x27;, | |
off: &#x27;OFF&#x27;, | |
} | |
function Toggle() { | |
const [clicksSinceReset, setClicksSinceReset] = React.useState(0) | |
const tooManyClicks = clicksSinceReset &gt;= 4 | |
const {on, toggle, setOn, setOff} = useToggle({ | |
reducer(currentState, action) { | |
if (tooManyClicks &amp;&amp; action.type === useToggle.types.toggle) { | |
// other changes are fine, but on needs to be unchanged | |
return {...action.changes, on: currentState.on} | |
} else { | |
// the changes are fine | |
return action.changes | |
} | |
}, | |
}) | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={setOff}&gt;Switch Off&lt;/button&gt; | |
&lt;button onClick={setOn}&gt;Switch On&lt;/button&gt; | |
&lt;Switch | |
onClick={() =&gt; { | |
toggle() | |
setClicksSinceReset(count =&gt; count + 1) | |
}} | |
on={on} | |
/&gt; | |
{tooManyClicks ? ( | |
&lt;button onClick={() =&gt; setClicksSinceReset(0)}&gt;Reset&lt;/button&gt; | |
) : null} | |
&lt;/div&gt; | |
) | |
} | |
function App() { | |
return &lt;Toggle /&gt; | |
} | |
ReactDOM.render(&lt;App /&gt;, document.getElementById(&#x27;root&#x27;)) | |
</code></pre><p>And here it is running in <a href="https://codesandbox.io/s/9j0pkq30lo">a codesandbox</a>:</p><p><iframe src="https://codesandbox.io/embed/9j0pkq30lo" style="width:100%;height:500px;border-width:0px;border-radius:4px;overflow:hidden"></iframe></p><p>Remember, what we&#x27;ve done here is enable users to hook into every state update | |
of our reducer to make changes to it. This makes our hook WAY more flexible, but | |
it also means that the way we update state is now part of the API and if we make | |
changes to how that happens, then it could be a breaking change for users. It&#x27;s | |
totally worth the trade-off for complex hooks/components, but it&#x27;s just good to | |
keep that in mind.</p><p>I hope you find patterns like this useful. Thanks to <code>useReducer</code>, this pattern | |
just kinda falls out (thank you React!). So give it a try on your codebase!</p><p>Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/the-state-reducer-pattern-with-react-hooks">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Super simple start to serverless]]></title> | |
<description><![CDATA[This last little while I've been doing a fair amount of work on | |
kentcdodds.com . One page that I've been working on is | |
the contact page where you can | |
request enterprise training , | |
schedule an hour of my time for consulting , or even | |
leave your…]]></description> | |
<link>https://kentcdodds.com/blog/super-simple-start-to-serverless</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/super-simple-start-to-serverless</guid> | |
<pubDate>Mon, 18 Mar 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>This last little while I&#x27;ve been doing a fair amount of work on | |
<a href="https://kentcdodds.com">kentcdodds.com</a>. One page that I&#x27;ve been working on is | |
the <a href="https://kentcdodds.com/contact">contact page</a> where you can | |
<a href="https://kcd.im/request-a-workshop">request enterprise training</a>, | |
<a href="https://kcd.im/consulting">schedule an hour of my time for consulting</a>, or even | |
<a href="https://kcd.im/testimonial">leave your own testimonial of my work</a> (I&#x27;d love it | |
if you did!).</p><p>My app is built with <a href="https://www.gatsbyjs.org">GatsbyJS</a> which has quickly | |
become one of my favorite pieces of technology. Gatsby is a static site | |
generator, so it generates a bunch of HTML, CSS, and JavaScript files in a | |
directory called <code>public</code> in my project and it leaves it to me to get those | |
files on the World Wide Web for you and your loved ones to enjoy.</p><p>So how do I get those files up there on the internet? I use | |
<a href="https://www.netlify.com">Netlify</a>! I&#x27;ve LOVED Netlify for years. It&#x27;s bonkers | |
how quickly I can get a site up and running (with HTTPS) with Netlify and it&#x27;s | |
integration with GitHub is unmatched. I love it.</p><p>Great, so now I&#x27;ve got static files served ultra-fast on the internet with | |
Netlify&#x27;s CDN. But how do I make it so my contact form can actually send emails? | |
You may not be aware of this, but web browsers are not able to send emails | |
themselves. To do that you need a server. I don&#x27;t know about you, but managing | |
servers and databases are among the top things on the list of stuff I do NOT | |
enjoy about web development.</p><p>You may have heard of &quot;Serverless&quot; or &quot;Lambda Functions&quot; or &quot;Azure Functions&quot; | |
etc. What&#x27;s cool about this is you can write your server code without worrying | |
about managing servers or paying to have them sit around waiting for requests | |
all day long (which is particularly useful for a contact form like mine).</p><p>I remember years ago when those started getting popular, I tried them out a bit | |
and was reminded why I don&#x27;t enjoy working with backend/devops stuff. As cool as | |
these functions were conceptually, I just didn&#x27;t feel like I was smart enough to | |
navigate the complexities of the offerings.</p><p>Luckily for us, almost exactly one year ago from the time I&#x27;m writing this, | |
Netlify took the complexity of AWS Lambda functions and made it super simple (I | |
think it&#x27;s what they do best):</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">🚀Today we’re bringing <a href="https://twitter.com/hashtag/serverless?src=hash&amp;ref_src=twsrc%5Etfw">#serverless</a> all the way to the frontend. Introducing AWS Lambda functions that deploy along with your site <a href="https://t.co/UmbXOYWVH6">https://t.co/UmbXOYWVH6</a> <a href="https://t.co/9heC614qVs">pic.twitter.com/9heC614qVs</a></p>— Netlify (@Netlify) <a href="https://twitter.com/Netlify/status/976126036491386880">March 20, 2018</a></blockquote></p><p>I want to give you a really quick intro to using Netlify to deploy server code | |
in a way that is mind-blowingly straightforward to me.</p><h2>How to use Netlify Lambda Functions</h2><p>Before you get started, you&#x27;ll need a <a href="https://github.com">GitHub</a> account, a | |
<a href="https://netlify.com">Netlify</a> account, and a fresh repository. Once you&#x27;ve got | |
the project locally on your computer, then you can start with the following | |
steps:</p><h3>Step 1: Create a <code>netlify.toml</code> file</h3><p>This file configures some stuff about our repository for netlify. Here&#x27;s all you | |
need for this to work:</p><pre><code class="language-toml">[build] | |
functions = &quot;./functions&quot; | |
</code></pre><p>All this is saying is: Hey Netlify, when you build my project, the functions are | |
located in the &quot;functions&quot; directory.</p><h3>Step 2: Create <code>functions/hello.js</code></h3><p>Now that we&#x27;ve told Netlify where to find our functions files, let&#x27;s make one:</p><pre><code class="language-javascript">// functions/hello.js | |
exports.handler = async event =&gt; { | |
const subject = event.queryStringParameters.name || &#x27;World&#x27; | |
return { | |
statusCode: 200, | |
body: `Hello ${subject}!`, | |
} | |
} | |
</code></pre><p>Netlify uses AWS Lambda under the hood, so you can checkout documentation and | |
learning materials about creating AWS Lambda functions for what kind of code can | |
go in there. We&#x27;ll start with something simple that returns a promise (note it&#x27;s | |
an <code>async</code> function) which resolves to an object for the response we want for | |
this function. You can do all kinds of things in that function (like connect | |
with an SMTP mail server and send an email which is what I do in my contact | |
form).</p><h3>Step 3: Commit and push</h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/e9b1e11a5b8456e30563dd79e4daa55e/26bc0/code.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:71.39664804469274%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="The above code in an editor" title="The above code in an editor" src="https://kentcdodds.com/static/e9b1e11a5b8456e30563dd79e4daa55e/17fa4/code.png" srcSet="https://kentcdodds.com/static/e9b1e11a5b8456e30563dd79e4daa55e/f4a45/code.png 259w,https://kentcdodds.com/static/e9b1e11a5b8456e30563dd79e4daa55e/ef0f6/code.png 518w,https://kentcdodds.com/static/e9b1e11a5b8456e30563dd79e4daa55e/17fa4/code.png 1035w,https://kentcdodds.com/static/e9b1e11a5b8456e30563dd79e4daa55e/d6f0c/code.png 1553w,https://kentcdodds.com/static/e9b1e11a5b8456e30563dd79e4daa55e/26bc0/code.png 1790w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Now you can just do: <code>git add -A</code>, then <code>git commit -m &#x27;init&#x27;</code>, and | |
<code>git push -u origin master</code> and you&#x27;re set! Now let&#x27;s go over to Netlify.</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/ccf05861e1dbb56b586f32bd7261ecaf/a6404/repo.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:40.20100502512563%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="The github repository" title="The github repository" src="https://kentcdodds.com/static/ccf05861e1dbb56b586f32bd7261ecaf/17fa4/repo.png" srcSet="https://kentcdodds.com/static/ccf05861e1dbb56b586f32bd7261ecaf/f4a45/repo.png 259w,https://kentcdodds.com/static/ccf05861e1dbb56b586f32bd7261ecaf/ef0f6/repo.png 518w,https://kentcdodds.com/static/ccf05861e1dbb56b586f32bd7261ecaf/17fa4/repo.png 1035w,https://kentcdodds.com/static/ccf05861e1dbb56b586f32bd7261ecaf/d6f0c/repo.png 1553w,https://kentcdodds.com/static/ccf05861e1dbb56b586f32bd7261ecaf/a6404/repo.png 1990w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><h3>Step 4: Setup Netlify</h3><p>Go to <a href="https://app.netlify.com">your netlify sites</a> and click &quot;New site from | |
Git.&quot; From there you&#x27;ll connect your Netlify account to your GitHub account and | |
it&#x27;ll show you a list of repositories. Search for the one you want and click on | |
it.</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/02d9cba6a199ed7ccbb02707809b7d8f/e9603/create-new-site.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:122.63610315186246%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Create New Site page" title="Create New Site page" src="https://kentcdodds.com/static/02d9cba6a199ed7ccbb02707809b7d8f/17fa4/create-new-site.png" srcSet="https://kentcdodds.com/static/02d9cba6a199ed7ccbb02707809b7d8f/f4a45/create-new-site.png 259w,https://kentcdodds.com/static/02d9cba6a199ed7ccbb02707809b7d8f/ef0f6/create-new-site.png 518w,https://kentcdodds.com/static/02d9cba6a199ed7ccbb02707809b7d8f/17fa4/create-new-site.png 1035w,https://kentcdodds.com/static/02d9cba6a199ed7ccbb02707809b7d8f/e9603/create-new-site.png 1396w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Go ahead and leave the &quot;Build command&quot; and &quot;Publish directory&quot; fields empty and | |
ignore the advanced options. Just click &quot;Deploy site.&quot;</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/0689a/overview.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:95.10344827586206%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Overview page" title="Overview page" src="https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/17fa4/overview.png" srcSet="https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/f4a45/overview.png 259w,https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/ef0f6/overview.png 518w,https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/17fa4/overview.png 1035w,https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/d6f0c/overview.png 1553w,https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/a1bed/overview.png 2070w,https://kentcdodds.com/static/c4b6281628dd131a659ca4ef905d6c37/0689a/overview.png 2900w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Now you&#x27;re on the Overview page of your site. It should only take a few seconds | |
to deploy your site. You can see info about that on the &quot;Deploys&quot; page. While | |
you wait, you can edit the Site name to be something more memorable by clicking | |
&quot;Site settings.&quot;</p><p>Once the deploy is finished, you can go to the &quot;Functions&quot; page:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/de157/functions.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:74.12008281573499%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Functions page showing hello.js" title="Functions page showing hello.js" src="https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/17fa4/functions.png" srcSet="https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/f4a45/functions.png 259w,https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/ef0f6/functions.png 518w,https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/17fa4/functions.png 1035w,https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/d6f0c/functions.png 1553w,https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/a1bed/functions.png 2070w,https://kentcdodds.com/static/c13684b6820a648abd6c20d9740c9e38/de157/functions.png 2898w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>There you should see <code>hello.js</code>! If you click that it&#x27;ll show you the function | |
log. Calls from your function to <code>console.log</code> should appear here with a | |
timestamp as well as a note for whenever your function is invoked:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/203a6/function-log.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:36.661211129296234%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Function log page" title="Function log page" src="https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/17fa4/function-log.png" srcSet="https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/f4a45/function-log.png 259w,https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/ef0f6/function-log.png 518w,https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/17fa4/function-log.png 1035w,https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/d6f0c/function-log.png 1553w,https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/a1bed/function-log.png 2070w,https://kentcdodds.com/static/b7bc63253c904e6a70bac9e67e9f2125/203a6/function-log.png 2444w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Checkout the &quot;Endpoint&quot; and it shows your function is available on the World | |
Wide Web at <code>https://&lt;your-site-name&gt;.netlify.com/.netlify/functions/hello</code>.</p><p>Really quick I just want to explain something. In <code>netlify.toml</code> we told Netlify | |
that it can find the JavaScript for our functions in the <code>./functions</code> directory | |
(we could&#x27;ve called that whatever we wanted). Netlify takes each JavaScript file | |
in there and deploys it (as-is) to AWS lambda, then sets up an endpoint for | |
hitting that function for us under <code>/.netlify/functions</code> (this is not | |
configurable AFAIK).</p><p>Cool, so now we can hit that URL and boom, Hello Bob Ross!</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/5ed5200a75528ced31143d6e12b4e39b/13bfc/function-output.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:44.04040404040405%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Function output saying Hello Bob Ross" title="Function output saying Hello Bob Ross" src="https://kentcdodds.com/static/5ed5200a75528ced31143d6e12b4e39b/17fa4/function-output.png" srcSet="https://kentcdodds.com/static/5ed5200a75528ced31143d6e12b4e39b/f4a45/function-output.png 259w,https://kentcdodds.com/static/5ed5200a75528ced31143d6e12b4e39b/ef0f6/function-output.png 518w,https://kentcdodds.com/static/5ed5200a75528ced31143d6e12b4e39b/17fa4/function-output.png 1035w,https://kentcdodds.com/static/5ed5200a75528ced31143d6e12b4e39b/d6f0c/function-output.png 1553w,https://kentcdodds.com/static/5ed5200a75528ced31143d6e12b4e39b/13bfc/function-output.png 1980w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Wahoo!</p><h2>Next steps</h2><p>You can find my version of this project | |
<a href="https://github.com/kentcdodds/super-simple-netlify-function-example">on GitHub</a> | |
and | |
<a href="https://simple-fn-example.netlify.com/.netlify/functions/hello">on Netlify</a>.</p><p>One important thing to note is that Netlify will send only the files for our | |
functions to AWS and no more. This means that if you try to <code>require</code> something | |
from a local <code>utils</code> directory or <code>node_modules</code> you&#x27;re out of luck.</p><p>Also, testing this locally is... not easy...</p><p><strong>But wait!</strong> Netlify engineers have built | |
<a href="https://www.npmjs.com/package/netlify-lambda"><code>netlify-lambda</code></a> to solve both | |
of these problems! I&#x27;ve taken the liberty of giving you an example of this same | |
function that uses <code>netlify-lambda</code> here | |
<a href="https://github.com/kentcdodds/netlify-function-example">on GitHub</a>.</p><p>Here are the main changes:</p><ol><li>Added a <code>package.json</code> to configure npm/yarn to install <code>netlify-lambda</code> and | |
created <code>dev</code> and <code>build</code> scripts.</li><li>Added a <code>.gitignore</code> to ignore the <code>.netlify</code> directory which is where | |
<code>netlify-lambda</code> will place our compiled functions.</li><li>Added a <code>command</code> line in our <code>netlify.toml</code> file to have the build run the | |
command <code>npm run build</code> which will trigger <code>netlify-lambda</code> to build our | |
function into a single file for delivery to AWS Lambda.</li><li>Updated the <code>functions</code> line in our <code>netlify.toml</code> file to point to | |
<code>.netlify/functions/</code> which is where <code>netlify-lambda</code> will place our final | |
built functions.</li></ol><p>Everything else is the same, but now we can actually require different files!</p><h2>Conclusion</h2><p>Any time we need to make a change to our function, we simply make those changes, | |
commit them to GitHub and Netlify will automatically update that on AWS Lambda. | |
It&#x27;s very fast and super empowering.</p><p>I love enabling technologies like this. I shouldn&#x27;t have to build an entire | |
backend with a database and server farm to allow you to send me | |
<a href="https://kcd.im/request-a-workshop">Enterprise Training Inquiries</a>, and thanks | |
to Netlify, I don&#x27;t have to. It&#x27;s pretty slick!</p><p>Give it a try! I think you&#x27;ll love it too.</p><h2>P.S.</h2><p>My friend <a href="https://twitter.com/swyx">Shawn Wang</a> works at Netlify and he | |
mentioned he&#x27;s working on a few neat things that he&#x27;ll be announcing at | |
<a href="https://twitter.com/jamstackconf">jamstackconf</a> 2019. He gave me a discount | |
code you can use to get $100 off a ticket if you want to go: <code>friendsofswyx</code>.</p><p>Also, Shawn mentioned that Netlify does have a Forms service built-in for | |
contact forms like I have. I originally used that, but it doesn&#x27;t actually send | |
emails (you have to integrate with Zapier and it&#x27;s an ok experience).</p><p>Also, while <code>/.netlify/functions</code> is not configurable, you can alias it to | |
anything with a redirect, including serving your entire site from a function | |
which is pretty legit.</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/super-simple-start-to-serverless">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How to get started with programming]]></title> | |
<description><![CDATA[Programming computers is amazing. There are so many things you can accomplish | |
with technology these days and being able to control what computers, phones, and | |
IoT devices do enables you | |
to accomplish so much more. There's no shortage of ideas of…]]></description> | |
<link>https://kentcdodds.com/blog/how-to-get-started-with-programming</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-to-get-started-with-programming</guid> | |
<pubDate>Wed, 06 Mar 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Programming computers is amazing. There are so many things you can accomplish | |
with technology these days and being able to control what computers, phones, and | |
<a href="https://en.wikipedia.org/wiki/Internet_of_things">IoT devices</a> do enables you | |
to accomplish so much more.</p><p>There&#x27;s no shortage of ideas of the things that we can accomplish with | |
technology. What&#x27;s super cool about knowing how to program is the knowledge that | |
given enough time you can create your idea with technology yourself. Whether | |
that&#x27;s <a href="https://kentcdodds.com/blog/building-production-apps-100-in-the-browser">a simple todo app</a> | |
with your specific requirements or the next Instagram, when you know how to | |
program, the possibilities are seemingly endless.</p><p>Now, let&#x27;s not get ahead of ourselves. There&#x27;s a reason that big tech companies | |
employ hundreds or even thousands of software programmers and it&#x27;s because | |
building cool things like this is not easy. That said, there&#x27;s so much good that | |
you can do with software, it&#x27;s definitely worthwhile learning.</p><h2>So, where do I start?</h2><p>There is so much that you can do with programming these days that where you go | |
kinda depends on what you want to do with your coding skills. But regardless of | |
where you want to go with programming, there are some fundamental things you can | |
learn at the beginning to get your feet wet.</p><p>Whatever you end up doing to get started, I want to give you a suggestion:</p><blockquote><p><strong>Don&#x27;t focus on learning to code. Focus on solving a problem that interests | |
you.</strong></p></blockquote><h2>Learning Backwards</h2><p>I call this <strong>&quot;learning backwards.&quot;</strong> By that I mean you start with what you | |
want to create, and learn the things you need to learn to accomplish your goals. | |
Doing things this way makes things much less abstract as you&#x27;re learning a | |
subject that is all about abstraction.</p><p>However, doing this backward learning might be tough for you for a few reasons:</p><ol><li>You have so many ideas and you&#x27;re not sure where to start.</li><li>Your ideas are so ambitious it&#x27;s overwhelming.</li><li>You have no ideas at all. Everything&#x27;s been done before.</li></ol><h2>Your first idea</h2><p>Here are some tips for combatting these issues. Just choose one idea and focus | |
on that one. The first program you write is going to have a LOT of problems. So | |
even if you do end up building a company out of this idea, it&#x27;s unlikely that | |
what you sell to customers will resemble your first iteration even in the | |
slightest. So avoid thinking that the first thing you build is going to be a | |
life changing creation (I mean, learning programming could be life changing for | |
you, but it&#x27;s unlikely to exist in it&#x27;s initial form forever).</p><blockquote><p><strong>Don&#x27;t be afraid of making mistakes; instead embrace the fact that you will. | |
And do so with your eyes wide open.</strong></p></blockquote><p>Also, don&#x27;t stress over this first idea. It doesn&#x27;t have to be a world shaking | |
idea. Here, let me give you a quick and easy way to come up with an idea. Think | |
of one of your hobbies. What&#x27;s one repetitive task you find yourself doing | |
regularly with that hobby? Build a simple program that helps you do that | |
repetitive task so it takes less time.</p><p>Here&#x27;s an example from my own life: I like playing board/card games and we have | |
lots of games like this, so we sometimes struggle deciding what to play. So I | |
could build a program that can help us decide which one to play by categorizing | |
our games and helps us decide by asking questions and then suggesting a few | |
options based on our answers.</p><h2>Now learn what you need to learn to build it</h2><p>Here&#x27;s where things start to get a bit hard. Now that you have an idea of a | |
simple program that you want to build, you have a lot to learn. There are many | |
ways you can go about getting the information you need. In particular there are | |
many free resources available to learn the basics of programming.</p><h3>Foundational programming concepts</h3><p>One resource I suggest is <a href="https://scratch.mit.edu">Scratch</a> which is a | |
surprisingly powerful programming-like platform that doesn&#x27;t require writing any | |
code at all. It&#x27;s a great place to start learning about foundational programming | |
concepts like variables, loops, conditional statements, and events. You will | |
likely not even know that you&#x27;re learning these things as you build stories and | |
games with the platform, but they will come to you and what you learn will help | |
you as you move onto the next phase of learning and getting your first idea | |
built.</p><h3>Writing a simple program</h3><p>Now you need to make a decision: what programming language do I use? Might I | |
suggest JavaScript. Not only because I&#x27;ve bet the farm on JavaScript myself, but | |
also because of all programming languages it strikes the best balance of | |
approachability, ubiquity, and capability. JavaScript is by far the most | |
commonly used programming language in the world | |
(<a href="https://insights.stackoverflow.com/survey/2018/#technology">Stack Overflow Survey 2018</a>). | |
There are a lot of resources for the JavaScript language which can help you as | |
you start on the long journey of learning to code.</p><p>Having chosen JavaScript as your programming language I&#x27;ll give you your first | |
opportunity to write some code now. You&#x27;re probably reading this in an internet | |
browser like Chrome, Safari, Microsoft Edge, etc. Each of these run JavaScript | |
and are capable of running your JavaScript code! Go ahead and Google this: &quot;How | |
to open the Developer Console in <code>&lt;your browser&gt;</code>&quot;</p><p>Here&#x27;s what it looks like in Google Chrome on Mac:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/f415ca6e870eb1a0754c5fc73556f8e7/b3a23/devtools-menu.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:31.24165554072096%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="developer tools menu" title="developer tools menu" src="https://kentcdodds.com/static/f415ca6e870eb1a0754c5fc73556f8e7/17fa4/devtools-menu.png" srcSet="https://kentcdodds.com/static/f415ca6e870eb1a0754c5fc73556f8e7/f4a45/devtools-menu.png 259w,https://kentcdodds.com/static/f415ca6e870eb1a0754c5fc73556f8e7/ef0f6/devtools-menu.png 518w,https://kentcdodds.com/static/f415ca6e870eb1a0754c5fc73556f8e7/17fa4/devtools-menu.png 1035w,https://kentcdodds.com/static/f415ca6e870eb1a0754c5fc73556f8e7/b3a23/devtools-menu.png 1498w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Clicking on that &quot;Developer Tools&quot; menu option opens up the Chrome Developer | |
Tools:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/bc452034bfd1c852ddef76ced41fbac8/cc3b5/devtools.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:33.29864724245578%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="developer tools elements panel" title="developer tools elements panel" src="https://kentcdodds.com/static/bc452034bfd1c852ddef76ced41fbac8/17fa4/devtools.png" srcSet="https://kentcdodds.com/static/bc452034bfd1c852ddef76ced41fbac8/f4a45/devtools.png 259w,https://kentcdodds.com/static/bc452034bfd1c852ddef76ced41fbac8/ef0f6/devtools.png 518w,https://kentcdodds.com/static/bc452034bfd1c852ddef76ced41fbac8/17fa4/devtools.png 1035w,https://kentcdodds.com/static/bc452034bfd1c852ddef76ced41fbac8/d6f0c/devtools.png 1553w,https://kentcdodds.com/static/bc452034bfd1c852ddef76ced41fbac8/cc3b5/devtools.png 1922w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Now, what you want to bring up is the &quot;Developer Console&quot; which here is labeled | |
&quot;Console.&quot; If you click on that, it&#x27;ll bring this up next:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/3262d167370e98682d1fe4a76931eb12/13bfc/devtools-console.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:32.62626262626262%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="developer tools console panel" title="developer tools console panel" src="https://kentcdodds.com/static/3262d167370e98682d1fe4a76931eb12/17fa4/devtools-console.png" srcSet="https://kentcdodds.com/static/3262d167370e98682d1fe4a76931eb12/f4a45/devtools-console.png 259w,https://kentcdodds.com/static/3262d167370e98682d1fe4a76931eb12/ef0f6/devtools-console.png 518w,https://kentcdodds.com/static/3262d167370e98682d1fe4a76931eb12/17fa4/devtools-console.png 1035w,https://kentcdodds.com/static/3262d167370e98682d1fe4a76931eb12/d6f0c/devtools-console.png 1553w,https://kentcdodds.com/static/3262d167370e98682d1fe4a76931eb12/13bfc/devtools-console.png 1980w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>And this is your first window into programming in JavaScript! That area there is | |
where you can start typing out some code. Here, try typing <code>1 + 2</code> and hit | |
&quot;enter&quot;</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:144px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/54352cecece35129a117493fa80ce2cd/b6dc4/1plus2.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:83.33333333333334%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="1 + 2 evaluating to 3" title="1 + 2 evaluating to 3" src="https://kentcdodds.com/static/54352cecece35129a117493fa80ce2cd/b6dc4/1plus2.png" srcSet="https://kentcdodds.com/static/54352cecece35129a117493fa80ce2cd/b6dc4/1plus2.png 144w" sizes="(max-width: 144px) 100vw, 144px"/> | |
</a> | |
</span></p><p>There you go! You just wrote your first line of JavaScript. Of course, there&#x27;s a | |
lot more to it than that, but that&#x27;s a great place to start writing some simple | |
code.</p><p>One last thing before we move on (because this isn&#x27;t really a tutorial), if you | |
want to save your work, you can do so by creating what&#x27;s called an &quot;HTML&quot; file | |
that uses a <code>&lt;script&gt;</code> tag in it. Go ahead and create a new file using a text | |
editor (like Notepad for Windows or TextEdit for macOS). Copy and paste this | |
into that file:</p><pre><code class="language-html">&lt;html&gt; | |
&lt;head&gt; | |
&lt;title&gt;My first program&lt;/title&gt; | |
&lt;/head&gt; | |
&lt;body&gt; | |
&lt;script&gt; | |
function add(a, b) { | |
return a + b | |
} | |
const result = add(1, 2) | |
const message = &#x27;The answer to 1 + 2 is&#x27; | |
console.log(message, result) | |
&lt;/script&gt; | |
&lt;/body&gt; | |
&lt;/html&gt; | |
</code></pre><blockquote><p>Note: make sure that you save it as a Plain Text file. To do this in TextEdit | |
click &quot;Make Plain Text&quot; under the &quot;Format&quot; menu before saving it.</p></blockquote><p>Go ahead and save that file as <code>my-first-program.html</code> onto your desktop. Then | |
open it up in your web browser and open the developer console on that page and | |
you should have something like this:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/43659e20fc212c507ae137d72bc2a6fa/126c8/my-first-program.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:82.83828382838283%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="web page with something logged to the console" title="web page with something logged to the console" src="https://kentcdodds.com/static/43659e20fc212c507ae137d72bc2a6fa/17fa4/my-first-program.png" srcSet="https://kentcdodds.com/static/43659e20fc212c507ae137d72bc2a6fa/f4a45/my-first-program.png 259w,https://kentcdodds.com/static/43659e20fc212c507ae137d72bc2a6fa/ef0f6/my-first-program.png 518w,https://kentcdodds.com/static/43659e20fc212c507ae137d72bc2a6fa/17fa4/my-first-program.png 1035w,https://kentcdodds.com/static/43659e20fc212c507ae137d72bc2a6fa/126c8/my-first-program.png 1212w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Now you can play around in that file, make changes to it, and reload your page | |
and your program will run every time you do it. This is a great place to start | |
playing around with coding in JavaScript.</p><blockquote><p>I recommend that you install a real programming text editor like | |
<a href="https://code.visualstudio.com">VSCode</a>. It will really improve your | |
experience writing JavaScript programs.</p></blockquote><h3>Learning the JavaScript language</h3><p>After you&#x27;ve become comfortable writing simple JavaScript code, it&#x27;s time to | |
start learning the JavaScript language. There are a lot of resources for this | |
and you need to find something that matches your learning style. I recommend | |
googling around a little bit. You&#x27;ll probably find some useful tutorials on | |
YouTube and there are lots of good books about JavaScript. One book that I can | |
suggest to you is the | |
<a href="https://github.com/getify/You-Dont-Know-JS">You Don&#x27;t Know JS</a> book series. | |
It&#x27;s pretty deep, but it&#x27;ll give you a fantastic foundational understanding of | |
the language.</p><p>There&#x27;s no shortcut to experience in coding I&#x27;m afraid. So the more time you | |
spend actually writing code, the better you&#x27;ll get at it. Keep at it! You can | |
totally do this.</p><h2>Now build your idea</h2><p>During this whole time, you should hopefully not have forgotten about the | |
original problem you wanted to solve. Once you&#x27;ve written a few simple programs, | |
try to solve your problem with what you&#x27;ve learned. What you build doesn&#x27;t have | |
to look amazing, or work 100% of the time. You can work your way up to the | |
perfection you&#x27;re hoping for. Build as simple of a version of what you&#x27;re | |
looking for as possible.</p><p><strong>The process of success/failure is where you&#x27;ll do the bulk of your learning,</strong> | |
so don&#x27;t feel bad if you&#x27;re really struggling to learn this stuff. That&#x27;s where | |
the real learning happens.</p><h2>Conclusion</h2><p>I hope this is helpful to you. I&#x27;m working on creating more resources for people | |
in your position myself, so I suggest you sign up for my newsletter here: | |
<a href="https://kcd.im/news">kcd.im/news</a>. Another tip for you is that in the software | |
industry, twitter is a pretty big platform where coders connect and communicate. | |
If you&#x27;re not on twitter yet, jump on board and I have a suggestion for your | |
first follow: <a href="https://twitter.com/kentcdodds">@kentcdodds</a> 😉</p><p>Good luck to you!</p><p>P.S. If you&#x27;d like to see more beginner material and tutorials from me, please | |
<a href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fkentcdodds.com%2Fblog%2Fhow-to-get-started-with-programming&amp;text=Hey,%20@kentcdodds,%20I%27d%20love%20it%20if%20you%20could%20create%20more%20beginner%20programmer%20content!">ask me about it on twitter</a></p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-to-get-started-with-programming">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How to Enable React Strict Mode]]></title> | |
<description><![CDATA[In January 2018, Brian Vaughn | |
added <React.StrictMode /> . | |
Here's how to start using it in your app today: Ok, so what does this do? Go ahead and give it a try in your app and see what | |
happens. Don't worry, I'll wait... What happens will be…]]></description> | |
<link>https://kentcdodds.com/blog/react-strict-mode</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/react-strict-mode</guid> | |
<pubDate>Mon, 04 Mar 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>In January 2018, <a href="https://twitter.com/brian_d_vaughn">Brian Vaughn</a> | |
<a href="https://github.com/facebook/react/pull/12083">added <code>&lt;React.StrictMode /&gt;</code></a>. | |
Here&#x27;s how to start using it in your app today:</p><pre><code class="language-diff"> ReactDOM.render( | |
- &lt;App /&gt;, | |
+ &lt;React.StrictMode&gt;&lt;App /&gt;&lt;/React.StrictMode&gt; | |
document.getElementById(&#x27;root&#x27;) | |
) | |
</code></pre><p>Ok, so what does this do? Go ahead and give it a try in your app and see what | |
happens. Don&#x27;t worry, I&#x27;ll wait...</p><p><img src="https://media.giphy.com/media/26gR1iYzSqtzcta4E/source.gif" alt="waiting..."/></p><p>What happens will be different for everyone, but here&#x27;s an example of what some | |
of you might have seen:</p><pre><code>Warning: A string ref, &quot;myDiv&quot;, has been found within a strict mode tree. String refs are a source of potential bugs and should be avoided. We recommend using createRef() instead. | |
in StringRef (created by App) | |
in StrictMode (created by App) | |
in App | |
Learn more about using refs safely here: | |
https://fb.me/react-strict-mode-string-ref | |
Warning: Unsafe lifecycle methods were found within a strict-mode tree: | |
in StrictMode (created by App) | |
in App | |
componentWillMount: Please update the following components to use componentDidMount instead: WillMount | |
componentWillReceiveProps: Please update the following components to use static getDerivedStateFromProps instead: WillReceiveProps | |
componentWillUpdate: Please update the following components to use componentDidUpdate instead: WillUpdate | |
Learn more about this warning here: | |
https://fb.me/react-strict-mode-warnings | |
Warning: Legacy context API has been detected within a strict-mode tree: | |
in StrictMode (created by App) | |
in App | |
Please update the following components: MyColorDiv, MyColorProvider | |
Learn more about this warning here: | |
https://fb.me/react-strict-mode-warnings | |
Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of FindDOMNode which is inside StrictMode. Instead, add a ref directly to the element you want to reference. | |
in div (created by FindDOMNode) | |
in FindDOMNode (created by App) | |
in StrictMode (created by App) | |
in App | |
Learn more about using refs safely here: | |
https://fb.me/react-strict-mode-find-node | |
</code></pre><p>And here&#x27;s the code that I used to generate those warnings:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import ReactDOM from &#x27;react-dom&#x27; | |
import PropTypes from &#x27;prop-types&#x27; | |
class WillMount extends React.Component { | |
componentWillMount() { | |
// Use componentDidMount instead | |
} | |
render() { | |
return null | |
} | |
} | |
class WillReceiveProps extends React.Component { | |
componentWillReceiveProps() { | |
// Use static getDerivedStateFromProps | |
} | |
render() { | |
return null | |
} | |
} | |
class WillUpdate extends React.Component { | |
componentWillUpdate() { | |
// Use componentDidUpdate instead | |
} | |
render() { | |
return null | |
} | |
} | |
class StringRef extends React.Component { | |
render() { | |
// Use React.createRef instead | |
return &lt;div ref=&quot;myDiv&quot; /&gt; | |
} | |
} | |
class FindDOMNode extends React.Component { | |
componentDidMount() { | |
// Use React.createRef instead | |
ReactDOM.findDOMNode(this) | |
} | |
render() { | |
return &lt;div /&gt; | |
} | |
} | |
class MyColorDiv extends React.Component { | |
// Use React.createContext().Consumer instead (or even better useContext) | |
static contextTypes = {color: PropTypes.string} | |
render() { | |
return &lt;div style={{color: this.context.color}} /&gt; | |
} | |
} | |
class MyColorProvider extends React.Component { | |
// Use React.createContext().Provider instead | |
static childContextTypes = {color: PropTypes.string} | |
getChildContext() { | |
return {color: &#x27;purple&#x27;} | |
} | |
render() { | |
return this.props.children | |
} | |
} | |
function App() { | |
return ( | |
&lt;&gt; | |
&lt;WillMount /&gt; | |
&lt;WillReceiveProps /&gt; | |
&lt;WillUpdate /&gt; | |
&lt;StringRef /&gt; | |
&lt;FindDOMNode /&gt; | |
&lt;MyColorProvider&gt; | |
&lt;MyColorDiv /&gt; | |
&lt;/MyColorProvider&gt; | |
&lt;/&gt; | |
) | |
} | |
ReactDOM.render( | |
&lt;React.StrictMode&gt; | |
&lt;App /&gt; | |
&lt;/React.StrictMode&gt;, | |
document.getElementById(&#x27;root&#x27;), | |
) | |
</code></pre><blockquote><p>And you can check this out for yourself in | |
<a href="https://codesandbox.io/s/y01q7vmpnz">this codesandbox</a>.</p></blockquote><p>Each of these warnings has a solid workaround that will make your code better in | |
various ways (most of them are related to concurrent mode which should hopefully | |
come to React later this year). Getting these warnings taken care of today will | |
make it much easier for you to upgrade to concurrent react bug-free when it | |
comes along.</p><blockquote><p>That said, don&#x27;t freak out if you have a ton of warnings in your app. Your | |
code will continue to work in the future. There&#x27;s also an <code>UNSAFE_</code> prefix for | |
those lifecycle methods you can use to silence the warning if you need. React | |
wont leave you in the dust here.</p></blockquote><h2>It runs code TWICE</h2><p>Another thing that React Strict Mode does is run certain callbacks/methods twice | |
(in DEV mode ONLY). You read that right! The following callbacks/methods will be | |
run twice in Strict Mode (in DEV mode ONLY):</p><ul><li>Class component <code>constructor</code> method</li><li>The <code>render</code> method (includes function components)</li><li><code>setState</code> updater functions (the first argument)</li><li>The static <code>getDerivedStateFromProps</code> lifecycle</li><li>The <code>React.useState</code> state initializer callback function</li><li>The <code>React.useMemo</code> callback</li></ul><blockquote><p>Checkout <a href="https://codesandbox.io/s/xvv55893mp">this codesandbox</a> which logs to | |
the console in hook callbacks and class methods to show you that certain | |
things happen twice.</p></blockquote><p>React does this because it cannot reliably warn you against side-effects you do | |
in those methods. But if those methods are idempotent, then calling them | |
multiple times shouldn&#x27;t cause any trouble. If they are not idempotent, then you | |
should notice funny things which you should hopefully be able to notice and fix.</p><blockquote><p>Note that <code>useEffect</code> and <code>useLayoutEffect</code> callbacks are <em>not</em> called twice | |
even in dev mode + strict mode because the entire point of those callbacks | |
<em>is</em> to perform side-effects.</p><p>Note that I also observed that the <code>reducer</code> you pass to <code>React.useReducer</code> is | |
<em>not</em> called twice in dev mode. I&#x27;m not sure why this is because I feel like | |
that could also benefit from this kind of warning.</p></blockquote><p>You&#x27;ll note that if you download either of my codesandbox projects and run the | |
<code>build</code> script (which enables production mode), all of the warnings go away and | |
the callbacks are only called once. This is because these are only there to help | |
you during development and will not impact you in production.</p><h2>Third Party Code</h2><p>Rendering your app in <code>React.StrictMode</code> will warn you when a component is using | |
a suboptimal method or API and it will help you catch things that can cause bugs | |
that can be hard to debug. But sometimes the code that&#x27;s violating strict mode | |
isn&#x27;t your own code, but code in a library.</p><p>So what do you do when you get a warning like this in a third-party component? I | |
recommend seeing how easy it would be to | |
<a href="http://makeapullrequest.com">open a PR</a> to the project. If that doesn&#x27;t work | |
out, then you could just &quot;vendor&quot; (download and commit it) or &quot;fork&quot; that | |
dependency and move on.</p><h2>Conclusion</h2><p>Remember, <strong>your code will continue to work</strong> whether you&#x27;re using strict mode | |
and fixing the warnings or not.</p><p>One approach that I think many teams are adopting (and I recommend) is to start | |
by wrapping parts of your app in <code>&lt;React.StrictMode /&gt;</code> instead of the entire | |
app:</p><pre><code class="language-jsx">function App() { | |
return ( | |
&lt;div&gt; | |
&lt;OldPartOfTheApp /&gt; | |
&lt;React.StrictMode&gt; | |
&lt;SomeNewFeature /&gt; | |
&lt;/React.StrictMode&gt; | |
&lt;AnotherOlderPartOfTheApp /&gt; | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>You can use <code>&lt;React.StrictMode /&gt;</code> anywhere in your app at any depth. This can | |
be great way to opt certain parts of your app into strict mode without getting a | |
ton of warnings everywhere.</p><p>I hope that doing this will help you catch bugs in your React codebases!</p><p>See you around 💯</p><blockquote><p>Read more about Strict Mode from | |
<a href="https://reactjs.org/docs/strict-mode.html">the react docs</a></p></blockquote> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/react-strict-mode">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Goodbye Medium]]></title> | |
<description><![CDATA[I've been a blogger for over 15 years. When I was a teenager, I had a blog on | |
blogger all about what Google was working on. I was one of the first bloggers to | |
report on Google's acquisition of YouTube (my blog got an insane number of views | |
that day…]]></description> | |
<link>https://kentcdodds.com/blog/goodbye-medium</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/goodbye-medium</guid> | |
<pubDate>Mon, 25 Feb 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I&#x27;ve been a blogger for over 15 years. When I was a teenager, I had a blog on | |
blogger all about what Google was working on. I was one of the first bloggers to | |
report on Google&#x27;s acquisition of YouTube (my blog got an insane number of views | |
that day).</p><p>I also used to write (on blogger) one called &quot;Google Video Highlights&quot; where I | |
and some random stranger I &quot;met&quot; on the internet would take turns posting random | |
videos that we found on Google Video (Google&#x27;s answer to YouTube before they | |
just bought YouTube).</p><p>I&#x27;ve had many blogs on many platforms since then: Blogger, wordpress.org, custom | |
wordpress, ghost, Jekyll, and finally Medium.com.</p><p>For me, blogging is all about convenience. I&#x27;ve always been more motivated to | |
create content than create a blogging platform, so I always looked for the | |
easiest way to do that. That said, I did want to differentiate myself (which is | |
why I tried wordpress). Every time I tried, I was reminded that building a | |
blogging platform was distracting me from creating content (not that building a | |
platform isn&#x27;t a great way to learn, it just wasn&#x27;t what I wanted to learn).</p><p>So when Medium became a thing years ago, I made the decision to go all in on | |
Medium. I was really happy with the authoring experience. It really removed | |
friction for me.</p><p>I started out on Medium at <code>medium.com/@kentcdodds</code>, but after a while, I | |
realized that I didn&#x27;t trust Medium to be around forever or (more likely) that I | |
would want to be on Medium forever. I knew it was inevitable that eventually | |
Medium would do some things with my content that I didn&#x27;t like. So about a year | |
or two ago I decided to create a &quot;kentcdodds&quot; publication and I used a (now | |
unsupported) feature of Medium to host my custom publication at a custom domain: | |
<code>blog.kentcdodds.com</code>.</p><p>I started publishing blog posts every week, and over 100 blog posts later, I | |
finally decided that the &quot;cost&quot; of creating my own blog platform was worth the | |
effort. Links to my articles on <code>blog.kentcdodds.com</code> have been shared with | |
millions of people all over the world on platforms like Twitter, Reddit, &quot;the | |
orange site,&quot; and on other platforms. Because I own the domain, <strong>I was able to | |
make this move without worrying about all those links breaking!</strong></p><blockquote><p>Note: this is actually the same reason I use <code>kcd.im</code> so much as well. I can | |
change where those URLs go if I ever need to which I definitely have. I | |
recommend | |
<a href="https://youtu.be/HL6paXyx6hM?index=40&amp;list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">you set up a URL shortener yourself</a> | |
(it&#x27;ll take you minutes and it&#x27;s totally free).</p></blockquote><p>I spent 1 hour building | |
<a href="https://github.com/kentcdodds/kcd-blog-redirector"><code>kcd-blog-redirector</code></a>. I | |
even livestreamed it on my new &quot;Coding with Kent&quot; series:</p><p><iframe width="100%" height="315" src="https://www.youtube-nocookie.com/embed/1EOj__JPN08?rel=0&amp;list=PLV5CVI1eNcJgJCEkMlsqXea6OIF_uV_ub" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></p><p>(You might think this would be a simple task, but it&#x27;s more complicated than you | |
think. Watch the first few minutes of the video and you&#x27;ll know what I mean.)</p><p>I spent MANY more hours working on kentcdodds.com and preparing | |
kentcdodds.com/blog. In the last few weeks, I&#x27;ve spent many hours working on | |
migrating all my old Medium posts from blog.kentcdodds.com to | |
kentcdodds.com/blog. (You can watch those on | |
<a href="https://kcd.im/coding">&quot;Coding with Kent&quot;</a> as well).</p><h2>Why leave Medium?</h2><p>So... What did Medium do that made it worth leaving? Well, | |
<a href="https://medium.com/@dan_abramov/why-my-new-blog-isnt-on-medium-3b280282fbae">Dan Abramov described it pretty well here</a>. | |
To sum up:</p><blockquote><ol><li>Some of my Medium articles unexpectedly got behind a paywall (this doesn&#x27;t | |
actually happen, but it&#x27;s an understandable misunderstanding 🤔)</li><li>My views on some topics have changed. (I also took the opportunity to leave | |
behind some old posts that aren&#x27;t really applicable to me anymore).</li><li>I want to dogfood React. (This is a fantastic place for me to play around | |
with React in a safe environment where it&#x27;s not a huge deal if I break | |
stuff).</li><li>I like to have full control over the experience. (This is a big one for | |
me!)</li><li>It’s open to the collaboration. (My blog is open source and every article | |
has a link where you can contribute fixes!)</li></ol></blockquote><p>I think one of the biggest things that bugs me about Medium is this:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Wait, why did I come to this site again? <a href="https://t.co/9rHce97Nvu">pic.twitter.com/9rHce97Nvu</a></p>— Nicholas C. Zakas (@slicknet) <a href="https://twitter.com/slicknet/status/1097584328962240512">February 18, 2019</a></blockquote></p><p>This is CRAZY. Like, what? I definitely want control over how my content is | |
consumed.</p><h2>So what now?</h2><p>The blog has several things that I need to work on still:</p><ol><li><a href="https://github.com/kentcdodds/kentcdodds.com/issues/48">Search</a></li><li><a href="https://github.com/kentcdodds/kentcdodds.com/issues/49">Category Pages</a></li><li><a href="https://github.com/kentcdodds/kentcdodds.com/issues/50">Keyword Pages</a></li><li><a href="https://github.com/kentcdodds/kentcdodds.com/issues/51">Fix the RSS Feed</a></li><li>Figure out how to automate sending emails that include nice syntax | |
highlighting</li></ol><p>There&#x27;s a lot more (no, I&#x27;m not planning on adding comments, we can chat on | |
twitter). What&#x27;s cool is that with amazing tools like Gatsby, I feel empowered | |
to build this stuff without sinking crazy amounts of time into it and building a | |
mess.</p><h2>How can I help?</h2><p>I&#x27;m so glad you asked! I know for a fact that some of the articles didn&#x27;t import | |
very well. The import process from Medium to Markdown is... imperfect (actually | |
it&#x27;s downright terrible), so there are some issues with some of the markdown.</p><p>If you&#x27;d like to help, I&#x27;d love your help making sure things look ok and if they | |
don&#x27;t fixing them.</p><p><strong>So if you&#x27;d like to help,</strong> I invite you to | |
<a href="https://docs.google.com/spreadsheets/d/1Fro-0x305nDsnoYuhid4qJRgcDb1a7-uXTvAU6EEa3U/edit?usp=sharing">join us on this Google Doc here</a> | |
and checkout the old Medium Post and compare it to the Imported post. Then fill | |
out the &quot;Looks good?&quot; column with either Yes, No Issue(s) Reported, or Pull | |
Request(s) Created. (There&#x27;s even a link for editing the post yourself if you | |
want!)</p><p>I&#x27;m looking forward to working on this further and I&#x27;m also really looking | |
forward to some of the content that I&#x27;m going to be bringing you to egghead.io | |
and... other places :)</p><p><strong>Stuff not to miss</strong>:</p><ul><li><a href="https://thinkster.io/tutorials/one-day-introduction-to-react-with-kent-c-dodds">One Day Introduction to React with Kent C. Dodds</a>: | |
This is a recorded workshop from back in October. If you&#x27;re totally new to | |
React and want a hands-on approach to learning it, this will be very helpful | |
to you! (There&#x27;s also | |
<a href="https://www.google.com/url?q=https://thinkster.io/pro/yearly/kcd-react-workshop">a HUGE sale right now on thinkster</a>)</li><li><a href="https://ti.to/thinkster-io/react-hooks-workshop-slc-may-2019">I&#x27;M GIVING A HOOKS WORKSHOP IN SALT LAKE CITY!!!!!</a>: | |
Both beginner and advanced. Check it out!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/goodbye-medium">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React Hooks: Compound Components]]></title> | |
<description><![CDATA[A few weeks ago I did a DevTips with Kent livestream | |
where I show you how to refactor the compound components pattern from a class | |
component to a function component with React hooks: https://youtu.be/415EfGPuhSo?list=PLV5CVI1eNcJgCrPH_e6d…]]></description> | |
<link>https://kentcdodds.com/blog/compound-components-with-react-hooks</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/compound-components-with-react-hooks</guid> | |
<pubDate>Mon, 18 Feb 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>A few weeks ago I did a <a href="https://kcd.im/devtips">DevTips with Kent</a> livestream | |
where I show you how to refactor the compound components pattern from a class | |
component to a function component with React hooks:</p><p><iframe width="100%" height="315" src="https://www.youtube-nocookie.com/embed/415EfGPuhSo?rel=0&amp;list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></p><p>If you&#x27;re unfamiliar with compound components, then you probably haven&#x27;t watched | |
my Advanced React Component Patterns course | |
<a href="http://kcd.im/advanced-react">on egghead.io</a> or | |
<a href="https://frontendmasters.com/courses/advanced-react-patterns">on Frontend Masters</a>.</p><p>The idea is that you have two or more components that work together to | |
accomplish a useful task. Typically one component is the parent, and the other | |
is the child. The objective is to provide a more expressive and flexible API.</p><p>Think of it like <code>&lt;select&gt;</code> and <code>&lt;option&gt;</code>:</p><pre><code class="language-html">&lt;select&gt; | |
&lt;option value=&quot;value1&quot;&gt;key1&lt;/option&gt; | |
&lt;option value=&quot;value2&quot;&gt;key2&lt;/option&gt; | |
&lt;option value=&quot;value3&quot;&gt;key3&lt;/option&gt; | |
&lt;/select&gt; | |
</code></pre><p>If you were to try and use one without the other it wouldn&#x27;t work (or make | |
sense). Additionally it&#x27;s actually a really great API. Let&#x27;s check out what it | |
would look like if we didn&#x27;t have a compound components API to work with | |
(remember, this is HTML, not JSX):</p><pre><code class="language-html">&lt;select options=&quot;key1:value1;key2:value2;key3:value3&quot;&gt;&lt;/select&gt; | |
</code></pre><p>I&#x27;m sure you can think of other ways to express this, but yuck. And how would | |
you express the <code>disabled</code> attribute with this kind of API? It&#x27;s kinda madness.</p><p>So the compound components API gives you a nice way to express relationships | |
between components.</p><p>Another important aspect of this is the concept of &quot;implicit state.&quot; The | |
<code>&lt;select&gt;</code> element implicitly stores state about the selected option and shares | |
that with it&#x27;s children so they know how to render themselves based on that | |
state. But that state sharing is implicit because there&#x27;s nothing in our HTML | |
code that can even access the state (and it doesn&#x27;t need to anyway).</p><p>Alright, let&#x27;s get a look at a legit React component that exposes a compound | |
component to understand these principles further. Here&#x27;s an example of | |
<a href="https://ui.reach.tech/menu-button">the <code>&lt;Menu /&gt;</code> component from Reach UI</a> that | |
exposes a compound components API:</p><pre><code class="language-jsx">function App() { | |
return ( | |
&lt;Menu&gt; | |
&lt;MenuButton&gt; | |
Actions &lt;span aria-hidden&gt;▾&lt;/span&gt; | |
&lt;/MenuButton&gt; | |
&lt;MenuList&gt; | |
&lt;MenuItem onSelect={() =&gt; alert(&#x27;Download&#x27;)}&gt;Download&lt;/MenuItem&gt; | |
&lt;MenuItem onSelect={() =&gt; alert(&#x27;Copy&#x27;)}&gt;Create a Copy&lt;/MenuItem&gt; | |
&lt;MenuItem onSelect={() =&gt; alert(&#x27;Delete&#x27;)}&gt;Delete&lt;/MenuItem&gt; | |
&lt;/MenuList&gt; | |
&lt;/Menu&gt; | |
) | |
} | |
</code></pre><p>In this example, the <code>&lt;Menu&gt;</code> establishes some shared implicit state. The | |
<code>&lt;MenuButton&gt;</code>, <code>&lt;MenuList&gt;</code>, and <code>&lt;MenuItem&gt;</code> components each access and/or | |
manipulate that state, and it&#x27;s all done implicitly. This allows you to have the | |
expressive API you&#x27;re looking for.</p><p>So how is this done? Well, if you watch | |
<a href="https://kcd.im/advanced-react">my course</a> I show you two ways to do it. One | |
with <code>React.cloneElement</code> on the children and the other with React context. (My | |
course will need to be slightly updated to show how to do this with hooks). In | |
this blog post, I&#x27;ll show you how to create a simple set of compound components | |
using context.</p><p>When teaching a new concept, I prefer to use simple examples at first. So we&#x27;ll | |
use my favorite <code>&lt;Toggle&gt;</code> component example for this.</p><p>Here&#x27;s how our <code>&lt;Toggle&gt;</code> compound components are going to be used:</p><pre><code class="language-jsx">function App() { | |
return ( | |
&lt;Toggle onToggle={on =&gt; console.log(on)}&gt; | |
&lt;Toggle.On&gt;The button is on&lt;/Toggle.On&gt; | |
&lt;Toggle.Off&gt;The button is off&lt;/Toggle.Off&gt; | |
&lt;Toggle.Button /&gt; | |
&lt;/Toggle&gt; | |
) | |
} | |
</code></pre><p>You&#x27;ll notice that we&#x27;re using a <code>.</code> in our component names. That&#x27;s because | |
those components are added as static properties to the <code>&lt;Toggle&gt;</code> component. | |
Note that this is not at all a requirement of compound components (the <code>&lt;Menu&gt;</code> | |
components above do not do this). I just like doing this as a way to explicitly | |
communicate the relationship.</p><p>Ok, the moment you&#x27;ve all been waiting for, the actual full implementation of | |
compound components with context and hooks:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
// this switch implements a checkbox input and is not relevant for this example | |
import {Switch} from &#x27;../switch&#x27; | |
const ToggleContext = React.createContext() | |
function useEffectAfterMount(cb, dependencies) { | |
const justMounted = React.useRef(true) | |
React.useEffect(() =&gt; { | |
if (!justMounted.current) { | |
return cb() | |
} | |
justMounted.current = false | |
}, dependencies) | |
} | |
function Toggle(props) { | |
const [on, setOn] = React.useState(false) | |
const toggle = React.useCallback(() =&gt; setOn(oldOn =&gt; !oldOn), []) | |
useEffectAfterMount(() =&gt; { | |
props.onToggle(on) | |
}, [on]) | |
const value = React.useMemo(() =&gt; ({on, toggle}), [on]) | |
return ( | |
&lt;ToggleContext.Provider value={value}&gt; | |
{props.children} | |
&lt;/ToggleContext.Provider&gt; | |
) | |
} | |
function useToggleContext() { | |
const context = React.useContext(ToggleContext) | |
if (!context) { | |
throw new Error( | |
`Toggle compound components cannot be rendered outside the Toggle component`, | |
) | |
} | |
return context | |
} | |
function On({children}) { | |
const {on} = useToggleContext() | |
return on ? children : null | |
} | |
function Off({children}) { | |
const {on} = useToggleContext() | |
return on ? null : children | |
} | |
function Button(props) { | |
const {on, toggle} = useToggleContext() | |
return &lt;Switch on={on} onClick={toggle} {...props} /&gt; | |
} | |
// for convenience, but totally not required... | |
Toggle.On = On | |
Toggle.Off = Off | |
Toggle.Button = Button | |
</code></pre><p>Here&#x27;s this component in action:</p><iframe src="https://codesandbox.io/embed/9yp5p2z7yr" style="width:100%;height:500px;border-width:0px;border-radius:4px;overflow:hidden" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe><p>So the way this works is we create a context with React where we store the state | |
and a mechanism for updating the state. Then the <code>&lt;Toggle&gt;</code> component is | |
responsible for providing that context value to the rest of the react tree.</p><p>I&#x27;ll walkthrough this implementation and explain the particulars in a future | |
update to my Advanced React Component Patterns course. So keep an eye out for | |
that!</p><p>I hope that helps you get some ideas of ways you can make your component APIs | |
more expressive and useful. Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/compound-components-with-react-hooks">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[🚨 Big Announcement: I'm a full-time educator! 👨🏫]]></title> | |
<description><![CDATA[I've been teaching for as long as I can remember. I talk about this a lot in | |
"Why and How I started public speaking" , | |
but just know that I have a love of teaching, I've done it a lot, and I want to | |
do more. This is why I'm excited to announce that…]]></description> | |
<link>https://kentcdodds.com/blog/full-time-educator</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/full-time-educator</guid> | |
<pubDate>Sun, 17 Feb 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I&#x27;ve been teaching for as long as I can remember. I talk about this a lot in | |
<a href="https://kentcdodds.com/blog/why-and-how-i-started-public-speaking">&quot;Why and How I started public speaking&quot;</a>, | |
but just know that I have a love of teaching, I&#x27;ve done it a lot, and I want to | |
do more. <strong>This is why I&#x27;m excited to announce that I&#x27;ve gone full-time | |
educator!</strong></p><p>What does this mean? Well, it means that I&#x27;ll be able to spend my daytime | |
completely focused on making content that will <strong>help you level up your skills | |
as a developer</strong>. And it means that I can spend my nighttime with my wife and | |
kids (not that I wasn&#x27;t, but when I&#x27;ve worked on big things like | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a> my wife didn&#x27;t see much | |
of me in the evenings).</p><p>So what am I working on? Here are a few things I&#x27;m planning:</p><ol><li>Remote workshops. Lots of remote workshops. Stay tuned for more info on how | |
to sign up for those :)</li><li>More sites like <a href="https://testingjavascript.com">TestingJavaScript.com</a>. Stuff | |
for React, JavaScript, HTML, CSS, and more more more!</li><li>Quality improvements to <a href="https://kcd.im/devtips">DevTips with Kent</a> | |
livestreams!</li><li>Some time to write and publish my fantasy novels: | |
<a href="https://kcd.im/shurlan">Shurlan</a> ⚪</li><li>More <a href="https://kcd.im/egghead">egghead.io</a> and | |
<a href="https://kcd.im/fem">Frontend Masters</a> courses!</li><li>Improved curation of the mountain of content I&#x27;ve put together over the | |
years, making it easier for you to pull value out of the stuff I&#x27;m creating.</li><li>Quality improvements to this newsletter, my website, and blog. Also making it | |
easier to get value from that stuff.</li><li>There&#x27;s more... But I&#x27;m going to surprise you 😜</li></ol><h2>Conclusion</h2><p>I had amazing experiences and co-workers at PayPal. I built some things that I&#x27;m | |
really proud of while there. If you&#x27;re looking for a place to work, I highly | |
recommend you <a href="http://paypal.jobs">give PayPal a close look</a>.</p><p>I can&#x27;t wait to show you what I have in store for you. Just to get things going, | |
I made this egghead.io lesson today! Give it a watch 💯</p><p><a href="https://egghead.io/lessons/react-detect-user-activity-with-a-custom-useidle-react-hook?pl=react-hooks-and-suspense-650307f2"><strong>Detect user activity with a custom useIdle React Hook</strong></a></p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/ddd951f4a736a738df4aa48f25cb51c1/a1bed/use-idle.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:62.512077294685994%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="useIdle egghead lesson screenshot" title="useIdle egghead lesson screenshot" src="https://kentcdodds.com/static/ddd951f4a736a738df4aa48f25cb51c1/17fa4/use-idle.png" srcSet="https://kentcdodds.com/static/ddd951f4a736a738df4aa48f25cb51c1/f4a45/use-idle.png 259w,https://kentcdodds.com/static/ddd951f4a736a738df4aa48f25cb51c1/ef0f6/use-idle.png 518w,https://kentcdodds.com/static/ddd951f4a736a738df4aa48f25cb51c1/17fa4/use-idle.png 1035w,https://kentcdodds.com/static/ddd951f4a736a738df4aa48f25cb51c1/d6f0c/use-idle.png 1553w,https://kentcdodds.com/static/ddd951f4a736a738df4aa48f25cb51c1/a1bed/use-idle.png 2070w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</a> | |
</span></p><p>Good luck, and stay tuned!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/full-time-educator">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Should I useState or useReducer?]]></title> | |
<description><![CDATA[Whenever there are two things to do the same thing, people inevitably ask: "When | |
do I use one over the other?" For example | |
"When do I use useEffect and useLayoutEffect ?" | |
Or | |
"When do I use Unit, Integration, or E2E tests?" | |
Or | |
"When to use…]]></description> | |
<link>https://kentcdodds.com/blog/should-i-usestate-or-usereducer</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/should-i-usestate-or-usereducer</guid> | |
<pubDate>Mon, 11 Feb 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Whenever there are two things to do the same thing, people inevitably ask: &quot;When | |
do I use one over the other?&quot;</p><p>For example | |
<a href="https://kentcdodds.com/blog/useeffect-vs-uselayouteffect">&quot;When do I use <code>useEffect</code> and <code>useLayoutEffect</code>?&quot;</a> | |
Or | |
<a href="https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests">&quot;When do I use Unit, Integration, or E2E tests?&quot;</a> | |
Or | |
<a href="https://kentcdodds.com/blog/control-props-vs-state-reducers">&quot;When to use Control Props or State Reducers?&quot;</a></p><p>I think <code>useState</code> and <code>useReducer</code> are no exception to this at all. In fact, | |
<a href="https://twitter.com/immatthamlin">Matt Hamlin</a> already posted | |
<a href="https://matthamlin.me/blog/2019/february/why-you-should-useReducer">useReducer, don&#x27;t useState</a> | |
and he makes some great points there. I&#x27;d like to throw my hat in this | |
discussion though because | |
<a href="https://github.com/kentcdodds/ama/issues/587">I was asked about it on my AMA</a>.</p><blockquote><p>Congratulations on the new design of your website kentcdodds.com. Looking at | |
the source code of your website, in the | |
<a href="https://github.com/kentcdodds/kentcdodds.com/blob/ed55aea3a58d24813a59cf72d8ffbdfbd96f769e/src/components/Forms/Subscribe.js"><code>Subscribe</code></a> | |
component, you used useState hooks to handle the state of this component. My | |
question is, is not it more optimized to use a useReducer here instead of | |
several useState? If not, why?</p></blockquote><p>Here&#x27;s the top of that Subscribe component</p><pre><code class="language-jsx">const [submitted, setSubmitted] = React.useState(false) | |
const [loading, setLoading] = React.useState(false) | |
const [response, setResponse] = React.useState(null) | |
const [errorMessage, setErrorMessage] = React.useState(null) | |
</code></pre><p>Then there&#x27;s logic throughout the component for calling those state updater | |
functions with the appropriate data, like here:</p><pre><code class="language-jsx">async function handleSubmit(values) { | |
setSubmitted(false) | |
setLoading(true) | |
try { | |
const responseJson = await fetch(/* stuff */).then(r =&gt; r.json()) | |
setSubmitted(true) | |
setResponse(responseJson) | |
setErrorMessage(null) | |
} catch (error) { | |
setSubmitted(false) | |
setErrorMessage(&#x27;Something went wrong!&#x27;) | |
} | |
setLoading(false) | |
} | |
</code></pre><p>If I were to rewrite this to use <code>useReducer</code> then it would look like this:</p><pre><code class="language-jsx">const [state, dispatch] = React.useReducer(reducer, { | |
submitted: false, | |
loading: false, | |
response: null, | |
errorMessage: null, | |
}) | |
</code></pre><p>Then the <code>reducer</code> would look something like this:</p><pre><code class="language-jsx">const types = { | |
SUBMIT_STARTED: 0, | |
} | |
function reducer(state, action) { | |
switch (action.type) { | |
case types.SUBMIT_STARTED: { | |
return {...state, submitted: false, loading: true} | |
} | |
case types.SUBMIT_COMPLETE: { | |
return { | |
...state, | |
submitted: true, | |
response: action.response, | |
errorMessage: null, | |
loading: false, | |
} | |
} | |
case types.SUBMIT_ERROR: { | |
return { | |
...state, | |
submitted: false, | |
errorMessage: action.errorMessage, | |
loading: false, | |
} | |
} | |
default: { | |
return state | |
} | |
} | |
} | |
</code></pre><p>And the <code>handleSubmit</code> function would look like this:</p><pre><code class="language-jsx">async function handleSubmit(values) { | |
dispatch({type: types.SUBMIT_STARTED}) | |
try { | |
const responseJson = await fetch(/* stuff */).then(r =&gt; r.json()) | |
dispatch({type: types.SUBMIT_COMPLETE, response: responseJson}) | |
} catch (error) { | |
dispatch({type: types.SUBMIT_ERROR, errorMessage: &#x27;Something went wrong!&#x27;}) | |
} | |
} | |
</code></pre><p>Matt Hamlin brings up in his blog post a few benefits to <code>useReducer</code> over | |
<code>useState</code>:</p><ul><li>Easier to manage larger state shapes</li><li>Easier to reason about by other developers</li><li>Easier to test</li></ul><p>For this specific case I don&#x27;t think that the first or second point really | |
applies. Four elements of state is hardly a &quot;large state shape&quot; and the | |
before/after here is no easier or harder to &quot;reason about&quot; for me. I think | |
they&#x27;re equally simple/complex.</p><p>As for testing, I would definitely agree that you could test the <code>reducer</code> in | |
isolation and that could be a nice benefit if I were doing a bunch of business | |
logic in there, but I&#x27;m not really. It&#x27;s pretty simple there.</p><p>Typically I prefer to write higher-level integration-like tests, so I wouldn&#x27;t | |
want to write tests for that <code>reducer</code> in isolation and instead would test the | |
<code>&lt;Subscribe /&gt;</code> component and my tests would treat the reducer as an | |
implementation detail.</p><blockquote><p>Now if there were some complex business logic in that <code>reducer</code> or several | |
edge cases, then I definitely would want to test that in isolation (and I | |
would use <a href="https://github.com/atlassian/jest-in-case"><code>jest-in-case</code></a> to do | |
it!).</p></blockquote><p>I think there is one main situation in which I prefer <code>useState</code> over | |
<code>useReducer</code>:</p><p><strong>When prototyping/building the component and you&#x27;re not certain of the | |
implementation</strong></p><p>While building a new component, you&#x27;re often adding/removing state from that | |
component&#x27;s implementation. I think it would be harder to do that if you do this | |
with a reducer. Once you solidified what you want your component to look like | |
then you can go make the decision of whether converting from several <code>useState</code>s | |
to a <code>useReducer</code> makes sense. Additionally, maybe you&#x27;ll decide that | |
<code>useReducer</code> makes sense for some of it and a custom hook that uses <code>useState</code> | |
would make sense for other parts of your component logic. I find it&#x27;s almost | |
always better to wait until I know what my code is going to look like before I | |
start making abstractions.</p><p>Oh, and if you&#x27;re prototyping, the code can be as unmaintainable as you want :) | |
So who cares? Do what&#x27;s faster.</p><h2>One situation when <code>useReducer</code> is basically always better</h2><p><strong>If your one element of your state relies on the value of another element of | |
your state, then it&#x27;s almost always best to use <code>useReducer</code></strong></p><p>For example, imagine you have a tic-tac-toe game you&#x27;re writing. You have one | |
element of state called <code>squares</code> which is just an array of all the squares and | |
their value:</p><pre><code>[ | |
&#x27; &#x27;, &#x27;X&#x27;, &#x27;O&#x27;, | |
&#x27;X&#x27;, &#x27;O&#x27;, &#x27;X&#x27;, | |
&#x27; &#x27;, &#x27; &#x27;, &#x27;X&#x27; | |
] | |
</code></pre><p>and another called <code>xIsNext</code> which maintains the who&#x27;s turn it is. When a user | |
clicks on a square, how does your code know whether the <code>squares</code> array should | |
update to <code>X</code> or <code>O</code>? It determines this based on the <code>xIsNext</code> state. Because | |
of this, it&#x27;s easier to use a reducer because the reducer function can accept | |
all of the current state and use that current state (which includes <code>xIsNext</code>) | |
to determine the new state.</p><p>The benefits here are <em>mostly</em> just code aesthetic, but if you start adding | |
async behavior here, then the case for <code>useReducer</code> is even more strong. With | |
our tic-tac-toe game, you can reference the current value of <code>xIsNext</code> in the | |
closure, but if you are updating the <code>squares</code> state asynchronously, then you | |
could be working with stale values of state which may or may not be what you | |
want. Using a reducer completely removes this potential issue though, which is | |
why I say it&#x27;s basically always better to use a reducer if your state elements | |
depend on one another when they&#x27;re updated.</p><p>Here&#x27;s an example of tic-tac-toe with <code>useReducer</code>:</p><p><iframe src="https://codesandbox.io/embed/r1m6pz58mq" style="width:100%;height:500px;border-width:0px;border-radius:4px;overflow:hidden"></iframe></p><h2>Conclusion</h2><p>So what&#x27;s the answer? Really, it depends. <code>useState</code> is literally built on top | |
of <code>useReducer</code>. I don&#x27;t think there are any relevant performance concerns | |
between the two so it&#x27;s mostly a cosmetic/preferential decision.</p><p>While I conceptually like what Matt is encouraging, I think I may have a longer | |
threshold before I&#x27;ll reach for <code>useReducer</code> to replace my <code>useState</code>. I also | |
really appreciate Matt for including this:</p><blockquote><p>they both have benefits and fallbacks that depend entirely upon their use</p></blockquote><p>I think the best thing you can do to develop an intuition for when to reach for | |
one or the other is to feel the pain. Use them both and see how happy/sad they | |
make your life.</p><p>Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/should-i-usestate-or-usereducer">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Intentional Career Building]]></title> | |
<description><![CDATA[I've been thinking a lot about this newsletter and what I want to do with it. | |
One thing I want to do is provide you with some specific ideas of things you can | |
do to improve your skills and get what you want out of your career. So today I'm | |
going to…]]></description> | |
<link>https://kentcdodds.com/blog/intentional-career-building</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/intentional-career-building</guid> | |
<pubDate>Mon, 11 Feb 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I&#x27;ve been thinking a lot about this newsletter and what I want to do with it. | |
One thing I want to do is provide you with some specific ideas of things you can | |
do to improve your skills and get what you want out of your career. So today I&#x27;m | |
going to give you a few ideas of things you can do to be more intentional about | |
building your career (or *gasp* &quot;personal brand&quot;).</p><p>I didn&#x27;t start out intentionally seeking/tracking followers, but over time I | |
learned a few things that could gain me a wider reach. I realized that by | |
intentionally building upon my career and my personal brand I felt like I had | |
more job security. After building enough of a following, I&#x27;m much less worried | |
about what would happen if I were to suddenly be let go from my job. I could | |
just send you all and email and send out a tweet and because I&#x27;ve developed a | |
following of you good people who seem to think I know what I&#x27;m doing 1) people | |
would see it and 2) people would care. It also makes me feel more confident that | |
I don&#x27;t have to do a job that I would rather not do and I can have the | |
flexibility to choose between multiple great options. This is a great position | |
for a husband and father of four to be in.</p><p>So here are a few things that I&#x27;ve done that have worked out pretty well for me | |
and I think could be reproducible for many of you.</p><h2>Communicate</h2><p>Whether you&#x27;re building your clout inside your company or outside, communicating | |
your accomplishments is really important. You may have saved the company from | |
financial ruin by fixing that critical bug last night, but if nobody knows the | |
scope of what you did or even that you&#x27;re the one who did it then you&#x27;re not | |
going to receive the credit. You don&#x27;t have to be all cocky about it (that&#x27;s | |
pretty annoying), and if you weren&#x27;t the only one who helped make something | |
happen make sure you give credit where it&#x27;s due. At the end of the day, make | |
sure that people understand the value that you&#x27;re creating.</p><h2>Double dip</h2><p>I&#x27;m not talking about getting paid for the same work by two different companies | |
(that is probably a really bad idea and illegal I&#x27;m guessing). What I am saying | |
is that if someone at work asks you a question about testing a react component, | |
then maybe you can share your answer in a public gist on GitHub and send it to | |
your co-worker as well as twitter. Just an idea there. I do this ALL. THE. TIME.</p><h2>Create value, not spam</h2><p>I get questions about getting noticed and recognized a lot. | |
<a href="https://twitter.com/gulshansainis/status/1085854812694929408">This tweet thread in particular is very helpful in this regard</a>. | |
Remember that it takes a very long time to get noticed and when you only have a | |
few dozen followers on twitter or other platforms your awesome content can never | |
seem to get to the right places. But I can tell you that even when someone | |
shares awesome content with me, I&#x27;m less likely to look at it if I can tell | |
they&#x27;ve been spamming it out to everyone they can think of or they bug me about | |
it a lot.</p><p>Respect people and their time. They may not always be able to help you or give | |
your stuff the time of day. But be patient and keep creating things that are | |
solving real problems <em>YOU</em> are experiencing, and eventually that will resonate | |
with enough people that they don&#x27;t want to miss your next big value add.</p><h2>Own your content</h2><p>If you&#x27;re publishing to a domain you don&#x27;t control, then you should consider | |
making a change. I was luckily grandfathered into Medium&#x27;s custom domain thing | |
before they stopped doing that which is why my medium blog is at | |
<code>blog.kentcdodds.com</code> rather than <code>medium.com/@kentcdodds</code>. I am planning on | |
moving off of medium soon (UPDATE, I did: | |
<a href="https://kentcdodds.com/blog/goodbye-medium">Goodbye Medium</a>) and when I do I&#x27;ll easily be able to | |
redirect all my blog posts to my new custom platform because I own the domain | |
name that all the links on twitter and elsewhere are pointing to.</p><p>This is also why I care so much about having | |
<a href="https://youtu.be/HL6paXyx6hM?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">a URL shortener</a>. | |
When I switched from tinyletter.com to buttondown.email and then to | |
convertkit.com, I was able to simply update <code>kcd.im/news</code> to point to the new | |
sign-up page and all existing links started taking people to the right place.</p><p>Owning the domain and sharing links to domains that you own can be very | |
powerful.</p><h2>Timing</h2><p>This is very anecdotal, but I&#x27;ve found that the time of day/week that you share | |
your content can have an impact on who sees it. Consider that nobody&#x27;s looking | |
at twitter on Friday evenings and people often don&#x27;t look at articles over the | |
weekends (there&#x27;s a reason most newsletters are sent during the week days). I | |
typically try to announce my stuff earlier in the week and often in the morning | |
before many people in the US really get to working and before many people on the | |
other side of the pacific go to sleep.</p><h2>Consistency</h2><p>People often need to see your avatar associated with useful content multiple | |
times before they realize that they like what you&#x27;re producing. So pushing out | |
consistent useful stuff on multiple platforms is really helpful. As a part of | |
this, using a consistent name and avatar (that actually looks like you if | |
possible... I realize this is a luxury that some people do not have due to | |
terrible stalker situations 🙁) across platforms that you rarely change (mine | |
has been the same for... a long time) can really help people recognize who you | |
are and associate your content with you on each of those platforms. Also, having | |
a consistent username across those platforms is also helpful.</p><p>Another note about consistency is to consider the message you&#x27;re trying to | |
communicate to your audience and stick to that message as much as you can. Maybe | |
leave the cat and baby pictures for other platforms or accounts (I realize that | |
nobody&#x27;s a mono-dimensional person and we should embrace our multi-dimensional | |
selves, but this is just something that I&#x27;ve noticed has an affect on my | |
effectiveness at reaching people so I thought I&#x27;d mention it).</p><p>As an example, I&#x27;ve recently gotten into writing a novel. I&#x27;ve tweeted about it | |
a lot. I decided that I&#x27;m going to get more serious about this novel writing | |
stuff and will probably be tweeting about it more, so I created | |
<a href="https://twitter.com/kent_writes">a new twitter account @kent_writes for the purpose</a>.</p><h2>Solve real problems <em>you</em> are having</h2><p>This one carries with it the assumption that you actually have problems which | |
don&#x27;t already have solutions. The npm ecosystem is full of solutions and you can | |
probably find solutions to many use cases already. Don&#x27;t bother spending a lot | |
of time building the next &quot;redux simplifier&quot; package because there are a million | |
of those out there and frankly I don&#x27;t personally find them interesting at all. | |
And don&#x27;t invent problems just so you can create solutions to them. Nobody will | |
care. Contribute to existing solutions, and solve problems where there are no | |
solutions or the existing solutions are lacking.</p><h2>Be patient</h2><p>Building your career clout/personal brand is important and takes a lot of time. | |
Be patient and humble. Lift others up and help people in your own way. | |
Eventually you will get recognized for it. I&#x27;m not saying you wont fall prey to | |
our societies natural and unfortunately biases. Many people have to deal with | |
those on a regular basis and it&#x27;s totally not fair. Keep working at it though | |
and you will find success.</p><p>I hope that&#x27;s helpful to you. Good luck.</p><hr/><p><strong>Call to action</strong>:</p><p>Choose one of the following things to do this week to be intentional about | |
building your career:</p><ul><li>Write the blog post you wish existed last week when you were learning | |
something new</li><li>Answer your co-worker&#x27;s question in a public space (YouTube, gist, etc.) and | |
share it</li><li>Write an email to your higher-ups describing the role you and your team mates | |
played in a recently completed project. Tell them you just want to share | |
something you were proud of and that you love working at a place where you can | |
tackle such challenges.</li><li>Go for a walk (sometimes you just need to take care of yourself and think)</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://dev.to/lkopacz/4-things-that-i-always-manually-test-2je8">4 things that I always manually test</a> | |
and | |
<a href="https://dev.to/lkopacz/a11y-and-js---a-seemingly-unconventional-romance-24i0">a11y and JS - A Seemingly Unconventional Romance</a> | |
both by <a href="https://twitter.com/littlekope0903">Lindsey Kopacz</a> are both terrific | |
articles about accessibility I recommend you read.</li></ul><blockquote><p><a href="https://twitter.com/kentcdodds/status/1095101380631584769">Share and participate in the discussion on twitter</a></p></blockquote> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/intentional-career-building">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Please stop building inaccessible forms (and how to fix them)]]></title> | |
<description><![CDATA[Note, today's blog post is very heavily inspired by the | |
Labeling Controls tutorial | |
from w3.org . HTML is accessible by default. This is true, with the important caveat that | |
when you use semantic HTML properly, what you've built will be accessible…]]></description> | |
<link>https://kentcdodds.com/blog/please-stop-building-inaccessible-forms-and-how-to-fix-them</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/please-stop-building-inaccessible-forms-and-how-to-fix-them</guid> | |
<pubDate>Mon, 04 Feb 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<blockquote><p>Note, today&#x27;s blog post is very heavily inspired by the | |
<a href="https://www.w3.org/WAI/tutorials/forms/labels">Labeling Controls tutorial</a> | |
from <a href="https://www.w3.org">w3.org</a>.</p></blockquote><p><strong>HTML is accessible by default.</strong> This is true, with the important caveat that | |
when you use semantic HTML properly, what you&#x27;ve built will be accessible. Now, | |
there are lots of ways that you can mess this up. Today I&#x27;m going to focus on | |
<code>&lt;label&gt;</code> and how to ensure that your form controls (inputs) are properly | |
labeled.</p><p>Before I get into that, allow me to just take a quick tangent and say this: | |
PLEASE USE YOUR FORM WITH NOTHING BUT THE KEYBOARD AND SEE IF IT IS POSSIBLE. So | |
many forms are impossible to use without a mouse and it drives me crazy. Lucky | |
for me I <em>can</em> use a mouse, but so many people in the world cannot. For them, it | |
is impossible to fill out your form.</p><p>Ok, so let&#x27;s talk about labels.</p><p>I&#x27;m regularly confronted with a form control that&#x27;s written like this:</p><pre><code class="language-html">Username: &lt;input /&gt; | |
</code></pre><p>To a seeing user, this looks fine, but to a blind user, they&#x27;ll need to use a | |
screen reader and without a label the user is left to their best guess as to | |
what the input is expecting when they focus on the input.</p><p>But it takes more than just putting the label text in a <code>&lt;label&gt;</code> input. So this | |
wont work:</p><pre><code class="language-html">&lt;label&gt;Username&lt;/label&gt; &lt;input /&gt; | |
</code></pre><blockquote><p>⚡️ You can know that the input has a label associated with it by checking the | |
input&#x27;s <code>labels</code> property or the label&#x27;s <code>control</code> property.</p></blockquote><p>Ok, here are four ways to associate the label with the input (in order of | |
personal preference):</p><h2>label[for] ➡ input[id]</h2><pre><code class="language-html">&lt;label for=&quot;username&quot;&gt;Username&lt;/label&gt; &lt;input id=&quot;username&quot; /&gt; | |
</code></pre><h2>input[aria-labeledby] ➡ label[id]</h2><pre><code class="language-html">&lt;label id=&quot;username&quot;&gt;Username&lt;/label&gt; &lt;input aria-labeledby=&quot;username&quot; /&gt; | |
</code></pre><h2>label 🤗 input</h2><blockquote><p>I like to think of this one as the label hugging the input</p></blockquote><pre><code class="language-html">&lt;label&gt; | |
Username | |
&lt;input /&gt; | |
&lt;/label&gt; | |
</code></pre><p>This one works fine and it&#x27;s nice because it means you don&#x27;t have to make | |
globally unique IDs (typically in a client-rendered app I&#x27;ll generate those | |
randomly anyway), but it&#x27;s not my favorite mostly because it can be harder to | |
style things the way I want them to, and | |
<a href="https://testing-library.com/docs/api-queries#getbylabeltext"><code>getByLabelText</code></a> | |
requires you provide a <code>selector</code> when you do this.</p><h2>input[aria-label]</h2><pre><code class="language-html">&lt;input aria-label=&quot;Username&quot; /&gt; | |
</code></pre><p>I don&#x27;t really like this approach because it removes a visible label which has | |
<em>other</em> accessibility implications.</p><h2>Conclusion</h2><p>You can actually also use the <code>title</code> attribute, but apparently screen readers | |
are inconsistent in considering this a label, so just stick with one of the | |
above methods.</p><p>As a bonus, when you properly associate a label to a form control, the clickable | |
area of the form control includes the label which is especially useful for | |
checkboxes and people on mobile devices.</p><p>Another bonus: you&#x27;ll be able to use | |
<a href="https://testing-library.com/docs/api-queries#getbylabeltext"><code>getByLabelText</code></a> | |
and that&#x27;ll | |
<a href="https://testing-library.com/docs/guiding-principles">make your tests resemble the way your software is used</a> | |
more closely which is great!</p><p>You can read more about this here: | |
<a href="https://www.w3.org/TR/WCAG20-TECHS/H44.html">Using label elements to associate text labels with form controls</a>.</p><p>I hope that&#x27;s helpful to you. Again, in addition to making sure that your form | |
controls are properly labeled, please please try using your form with a keyboard | |
only. They&#x27;re both very low hanging fruit and can make a big difference in the | |
accessibility of your forms.</p><blockquote><p><strong>Install and use | |
<a href="https://github.com/evcohen/eslint-plugin-jsx-a11y">eslint-plugin-jsx-a11y</a></strong></p></blockquote><p>Good luck! 💪</p><hr/><p><strong>Things to not miss</strong>:</p><ul><li>Join the <a href="https://kcd.im/shurlan-news">Shurlan News</a> mailing list for | |
irregular updates on the progress of my fantasy novel and a discount and early | |
access to the books when they&#x27;re published.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/please-stop-building-inaccessible-forms-and-how-to-fix-them">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Static vs Unit vs Integration vs E2E Testing for Frontend Apps]]></title> | |
<description><![CDATA[In my interview "Testing Practices with | |
J.B. Rainsberger " available on | |
TestingJavaScript.com he gave me a metaphor I | |
really like. He said: You can throw paint against the wall and eventually you might get most of the | |
wall, but until you go up to…]]></description> | |
<link>https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests</guid> | |
<pubDate>Mon, 28 Jan 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>In my interview &quot;Testing Practices with | |
<a href="https://twitter.com/jbrains">J.B. Rainsberger</a>&quot; available on | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a> he gave me a metaphor I | |
really like. He said:</p><blockquote><p>You can throw paint against the wall and eventually you might get most of the | |
wall, but until you go up to the wall with a brush, you&#x27;ll never get the | |
corners. 🖌️</p></blockquote><p>I love that metaphor in how it applies to testing because it&#x27;s basically saying | |
that choosing the right testing strategy is the same kind of choice you&#x27;d make | |
when choosing a brush for painting a wall. Would you use a fine-point brush for | |
the entire wall? Of course not. That would take too long and the end result | |
would probably not look very even. Would you use a roller to paint everything, | |
including around the mounted furnishings your great-great-grandmother brought | |
over the ocean a hundred years ago? No way. There are different brushes for | |
different use cases and the same thing applies to tests.</p><p>Nearly 1 year ago, | |
<a href="https://twitter.com/kentcdodds/status/960723172591992832">created the Testing Trophy</a>. | |
Since then <a href="https://twitter.com/Mappletons">Maggie Appleton</a> (the mastermind | |
behind <a href="https://egghead.io">egghead.io</a>&#x27;s masterful art/design) created this for | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>:</p><p><a href="https://testingjavascript.com"><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:102.4%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="The Testing Trophy" title="The Testing Trophy" src="https://kentcdodds.com/static/c56de32357ab41ab66d6feb2dfaec567/e11df/testing-trophy.png" srcSet="https://kentcdodds.com/static/c56de32357ab41ab66d6feb2dfaec567/f4a45/testing-trophy.png 259w,https://kentcdodds.com/static/c56de32357ab41ab66d6feb2dfaec567/ef0f6/testing-trophy.png 518w,https://kentcdodds.com/static/c56de32357ab41ab66d6feb2dfaec567/e11df/testing-trophy.png 1000w" sizes="(max-width: 1000px) 100vw, 1000px"/> | |
</span></a></p><p>In the Testing Trophy, there are 4 types of tests. It shows this text above, but | |
for the sake of those using assistive technologies (and in case the image fails | |
to load for you), I&#x27;ll write out what it says here from top to bottom:</p><ul><li><strong>End to End</strong>: A helper robot that behaves like a user to click around the | |
app and verify that it functions correctly. Sometimes called &quot;functional | |
testing&quot; or e2e.</li><li><strong>Integration</strong>: Verify that several units work together in harmony.</li><li><strong>Unit</strong>: Verify that individual, isolated parts work as expected.</li><li><strong>Static</strong>: Catch typos and type errors as you write the code.</li></ul><p>The size of these forms of testing on the trophy is relative to the amount of | |
focus you should give them when testing your applications (in general). I want | |
to take a deep dive on these different forms of testing, what it means | |
practically, and what we can do to optimize for the greatest bang for our | |
testing buck.</p><h2>Test Types</h2><p>Let&#x27;s look at a few examples of what these kinds of tests are</p><h3>End to End</h3><p>Typically these will run the entire application (both frontend and backend) and | |
your test will interact with the app just like a typical user would. These tests | |
are written with <a href="https://cypress.io">cypress</a>.</p><pre><code class="language-javascript">import {generate} from &#x27;todo-test-utils&#x27; | |
describe(&#x27;todo app&#x27;, () =&gt; { | |
it(&#x27;should work for a typical user&#x27;, () =&gt; { | |
const user = generate.user() | |
const todo = generate.todo() | |
// here we&#x27;re going through the registration process. | |
// I&#x27;ll typically only have one e2e test that does this. | |
// the rest of the tests will hit the same endpoint | |
// that the app does so we can skip navigating through that experience. | |
cy.visitApp() | |
.getByText(/register/i) | |
.click() | |
.getByLabelText(/username) | |
.type(user.username) | |
.getByLabelText(/password) | |
.type(user.password) | |
.getByText(/login/i) | |
.click() | |
.getByLabelText(/add todo/i) | |
.type(todo.description) | |
.type(&#x27;{enter}&#x27;) | |
.getByTestId(&#x27;todo-0&#x27;) | |
.should(&#x27;have.value&#x27;, todo.description) | |
.getByLabelText(&#x27;complete&#x27;) | |
.click() | |
.getByTestId(&#x27;todo-0&#x27;) | |
.should(&#x27;have.class&#x27;, &#x27;complete&#x27;) | |
// etc... | |
// My E2E tests typically behave similar to how a user would. | |
// They can sometimes be quite long. | |
}) | |
}) | |
</code></pre><h3>Integration</h3><p>The test below renders the full app. This is NOT a requirement of integration | |
tests and most of my integration tests don&#x27;t render the full app. They will | |
however render with all the providers used in my app (that&#x27;s what the <code>render</code> | |
method from the imaginary &quot;<code>til-client-test-utils</code>&quot; module does). The idea | |
behind integration tests is to mock as little as possible. I pretty much only | |
mock:</p><ol><li>Network requests (see <code>axiosMock</code>)</li><li>Components responsible for animation (because who wants to wait for that in | |
your tests?)</li></ol><pre><code class="language-javascript">import React from &#x27;react&#x27; | |
// this module is mocked via jest&#x27;s __mocks__ directory feature | |
import axiosMock from &#x27;axios&#x27; | |
import {render, generate, fireEvent} from &#x27;til-client-test-utils&#x27; | |
import {init as initAPI} from &#x27;../utils/api&#x27; | |
import App from &#x27;../app&#x27; | |
beforeEach(() =&gt; { | |
window.localStorage.removeItem(&#x27;token&#x27;) | |
axiosMock.__mock.reset() | |
initAPI() | |
}) | |
test(&#x27;login as an existing user&#x27;, async () =&gt; { | |
const { | |
getByTestId, | |
container, | |
getByText, | |
getByLabelText, | |
finishLoading, | |
} = render(&lt;App /&gt;) | |
// wait for the app to finish loading the mocked requests | |
await finishLoading() | |
fireEvent.click(getByText(/login/i)) | |
expect(window.location.href).toContain(&#x27;login&#x27;) | |
// fill out form | |
const fakeUser = generate.loginForm() | |
const usernameNode = getByLabelText(/username/i) | |
const passwordNode = getByLabelText(/password/i) | |
usernameNode.value = fakeUser.username | |
passwordNode.value = fakeUser.password | |
// submit form | |
const {post} = axiosMock.__mock.instance | |
const token = generate.token(fakeUser) | |
post.mockImplementationOnce(() =&gt; | |
Promise.resolve({ | |
data: {user: {...fakeUser, token}}, | |
}), | |
) | |
fireEvent.click(getByText(/submit/i)) | |
// wait for the mocked requests to finish | |
await finishLoading() | |
// assert calls | |
expect(axiosMock.__mock.instance.post).toHaveBeenCalledTimes(1) | |
expect(axiosMock.__mock.instance.post).toHaveBeenCalledWith( | |
&#x27;/auth/login&#x27;, | |
fakeUser, | |
) | |
// assert the state of the world | |
expect(window.localStorage.getItem(&#x27;token&#x27;)).toBe(token) | |
expect(window.location.href).not.toContain(&#x27;login&#x27;) | |
expect(getByTestId(&#x27;username-display&#x27;).textContent).toEqual(fakeUser.username) | |
expect(getByText(/logout/i)).toBeTruthy() | |
}) | |
</code></pre><h3>Unit</h3><pre><code class="language-javascript">import &#x27;react-testing-library/cleanup-after-each&#x27; | |
import &#x27;jest-dom/extend-expect&#x27; | |
import React from &#x27;react&#x27; | |
import {render} from &#x27;react-testing-library&#x27; | |
import ItemList from &#x27;../item-list&#x27; | |
// Some people don&#x27;t call these a unit test because we&#x27;re render to the DOM with React. | |
// They&#x27;d tell you to use shallow rendering instead. | |
// When they tell you this, send them to https://kcd.im/shallow | |
test(&#x27;renders &quot;no items&quot; when the item list is empty&#x27;, () =&gt; { | |
const {getByText} = render(&lt;ItemList items={[]} /&gt;) | |
expect(getByText(/no items/i)).toBeInTheDocument() | |
}) | |
test(&#x27;renders the items in a list&#x27;, () =&gt; { | |
const {getByText, queryByText} = render( | |
&lt;ItemList items={[&#x27;apple&#x27;, &#x27;orange&#x27;, &#x27;pear&#x27;]} /&gt;, | |
) | |
// note: with something so simple I might consider using a snapshot instead, but only if: | |
// 1. the snapshot is small | |
// 2. we use toMatchInlineSnapshot() | |
expect(getByText(/apple/i)).toBeInTheDocument() | |
expect(getByText(/orange/i)).toBeInTheDocument() | |
expect(getByText(/pear/i)).toBeInTheDocument() | |
expect(queryByText(/no items/i)).not.toBeInTheDocument() | |
}) | |
</code></pre><p>Everyone calls this a unit test and they&#x27;re right:</p><pre><code class="language-javascript">// pure functions are the BEST for unit testing and I LOVE using jest-in-case for them! | |
import cases from &#x27;jest-in-case&#x27; | |
import fizzbuzz from &#x27;../fizzbuzz&#x27; | |
cases( | |
&#x27;fizzbuzz&#x27;, | |
({input, output}) =&gt; expect(fizzbuzz(input)).toBe(output), | |
[ | |
[1, &#x27;1&#x27;], | |
[2, &#x27;2&#x27;], | |
[3, &#x27;Fizz&#x27;], | |
[5, &#x27;Buzz&#x27;], | |
[9, &#x27;Fizz&#x27;], | |
[15, &#x27;FizzBuzz&#x27;], | |
[16, &#x27;16&#x27;], | |
].map(([input, output]) =&gt; ({title: `${input} =&gt; ${output}`, input, output})), | |
) | |
</code></pre><h3>Static</h3><pre><code class="language-javascript">// can you spot the bug? | |
// I&#x27;ll bet ESLint&#x27;s for-direction rule could | |
// catch it faster than you in a code review 😉 | |
for (var i = 0; i &lt; 10; i--) { | |
console.log(i) | |
} | |
const two = &#x27;2&#x27; | |
// ok, this one&#x27;s contrived a bit, | |
// but TypeScript will tell you this is bad: | |
const result = add(1, two) | |
</code></pre><h2>Why do we test again?</h2><p>I think it&#x27;s important to remember why it is that we write tests in the first | |
place. Why do <em>you</em> write tests? Is it because I told you to? Is it because your | |
PR will get rejected unless it includes tests? Is it because testing enhances | |
your workflow?</p><p>The biggest and most important reason that I write tests is <strong>CONFIDENCE</strong>. I | |
want to be confident that the code I&#x27;m writing for the future wont break the app | |
that I have running in production today. So whatever I do, I want to make sure | |
that the kinds of tests I write bring me the most confidence possible and I need | |
to be cognizant of the trade-offs I&#x27;m making when testing.</p><h2>Let&#x27;s talk trade-offs</h2><p>There are some important elements to the testing trophy I want to call out in | |
this picture (ripped from <a href="http://kcd.im/confident-react">my slides</a>):</p><p><a href="https://slides.com/kentcdodds/confident-react#/3/5"><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1035px"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:70.73333333333333%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="The Testing Trophy with arrows indicating the trade-offs" title="The Testing Trophy with arrows indicating the trade-offs" src="https://kentcdodds.com/static/c331ec0658e3d2927db08b6e4946e266/17fa4/confidence-coefficient.png" srcSet="https://kentcdodds.com/static/c331ec0658e3d2927db08b6e4946e266/f4a45/confidence-coefficient.png 259w,https://kentcdodds.com/static/c331ec0658e3d2927db08b6e4946e266/ef0f6/confidence-coefficient.png 518w,https://kentcdodds.com/static/c331ec0658e3d2927db08b6e4946e266/17fa4/confidence-coefficient.png 1035w,https://kentcdodds.com/static/c331ec0658e3d2927db08b6e4946e266/8e846/confidence-coefficient.png 1500w" sizes="(max-width: 1035px) 100vw, 1035px"/> | |
</span></a></p><p>The arrows on the image signify three trade-offs you make when writing automated | |
tests:</p><h3>Cost: ¢ heap ➡ 💰🤑💰</h3><p>As you move up the testing trophy, the tests become more costly. This comes in | |
the form of actual money to run the tests in a continuous integration | |
environment, but also in the time it takes engineers to write and maintain each | |
individual test.</p><p>The higher up the trophy you go, the more points of failure there are and | |
therefore the more likely it is that a test will break, leading to more time | |
needed to analyze and fix the tests. <strong>Keep this in mind because it&#x27;s | |
important</strong> #foreshadowing...</p><h3>Speed: 🏎💨 ➡ 🐢</h3><p>As you move up the testing trophy, the tests typically run slower. This is due | |
to the fact that the higher you are on the testing trophy, the more code your | |
test is running. Unit tests typically test something small that has no | |
dependencies or will mock those dependencies (effectively swapping what could be | |
thousands of lines of code with only a few). <strong>Keep this in mind because it&#x27;s | |
important</strong> #foreshadowing...</p><h3>Confidence: Simple problems 👌 ➡ Big problems 😖</h3><p>The cost and speed trade-offs are typically referenced when people talk about | |
the testing pyramid 🔺. If those were the only trade-offs though, then I would | |
focus 100% of my efforts on unit tests and totally ignore any other form of | |
testing when regarding the testing pyramid. Of course we shouldn&#x27;t do that and | |
this is because of one super important principle that you&#x27;ve probably heard me | |
say before:</p><blockquote><p><a href="https://twitter.com/kentcdodds/status/977018512689455106">The more your tests resemble the way your software is used, the more confidence they can give you.</a></p></blockquote><p>What does this mean? It means that there&#x27;s no better way to ensure that your | |
Aunt Marie will be able to file her taxes using your tax software than actually | |
having her do it. But we don&#x27;t want to wait on Aunt Marie to find our bugs for | |
us right? It would take too long and she&#x27;d probably miss some features that we | |
should probably be testing. Compound that with the fact that we&#x27;re regularly | |
releasing updates to our software there&#x27;s no way any amount of humans would be | |
able to keep up.</p><p>So what do we do? <strong>We make trade-offs</strong>. And how do we do that? We write | |
software that tests our software. And the trade-off we&#x27;re always making when we | |
do that is now our tests don&#x27;t resemble the way our software is used as reliably | |
as when we had Aunt Marie testing our software. But we do it because we solve | |
real problems we had with that approach. And that&#x27;s what we&#x27;re doing at every | |
level of the testing trophy.</p><p><strong>As you move up the testing trophy, you&#x27;re increasing what I call the | |
&quot;confidence coefficient.&quot;</strong> This is the relative confidence that each test can | |
get you at that level. You can imagine that above the trophy is manual testing. | |
That would get you really great confidence from those tests, but the tests would | |
be really expensive and slow.</p><p>Earlier I told you to remember two things:</p><blockquote><ul><li>The higher up the trophy you go, the more points of failure there are and | |
therefore the more likely it is that a test will break</li><li>Unit tests typically test something small that has no dependencies or will | |
mock those dependencies (effectively swapping what could be thousands of | |
lines of code with only a few).</li></ul></blockquote><p>What those are saying is that the lower the trophy you are, the less code your | |
tests are testing. If you&#x27;re operating at a low level you need more tests to | |
cover the same number of lines of code in your application as a single test | |
could higher up the trophy. In fact, as you go lower down the testing trophy, | |
there are some things that are impossible to test.</p><p>In particular, static analysis tools are incapable of giving you confidence in | |
your business logic. Unit tests are incapable of ensuring that when you call | |
into a dependency that you&#x27;re calling it appropriately (though you can make | |
assertions on how it&#x27;s being called, you can&#x27;t ensure that it&#x27;s being called | |
properly with a unit test). UI Integration tests are incapable of ensuring that | |
you&#x27;re passing the right data to your backend and that you respond to and parse | |
errors correctly. End to End tests are pretty darn capable, but typically you&#x27;ll | |
run these in a non-production environment (production-like, but not production) | |
to trade-off that confidence for practicality.</p><p>Let&#x27;s go the other way now. At the top of the testing trophy, if you try to use | |
an E2E test to check that typing in a certain field and clicking the submit | |
button for an edge case in the integration between the form and the URL | |
generator, you&#x27;re doing a lot of setup work by running the entire application | |
(backend included). That might be more suitable for an integration test. If you | |
try to use an integration test to hit an edge case for the coupon code | |
calculator, you&#x27;re likely doing a fair amount of work in your setup function to | |
make sure you can render the components that use the coupon code calculator and | |
you could cover that edge case better in a unit test. If you try to use a unit | |
test to verify what happens when you call your add function with a string | |
instead of a number you could be much better served using a static type checking | |
tool like TypeScript.</p><h2>Conclusion</h2><p>Every level comes with its own trade-offs. An E2E test has more points of | |
failure making it often harder to track down what code caused the breakage, but | |
it also means that your test is giving you more confidence. This is especially | |
useful if you don&#x27;t have as much time to write tests. I&#x27;d rather have the | |
confidence and be faced with tracking down why it&#x27;s failing, than not having | |
caught the problem via a test in the first place.</p><p>In the end <strong>I don&#x27;t really care about the distinctions.</strong> If you want to call | |
my unit tests integration tests or even E2E tests (as some people have 🤷♂️) then | |
so be it. What I&#x27;m interested in is whether I&#x27;m confident that when I ship my | |
changes, my code satisfies the business requirements and I&#x27;ll use a mix of the | |
different testing strategies to accomplish that goal.</p><p>Good luck!</p><hr/><p><strong>Learn more about Testing from me</strong>:</p><p>Here are a few relevant blog posts for you as well:</p><ul><li><a href="https://kentcdodds.com/blog/testing-implementation-details">Testing Implementation Details</a></li><li><a href="https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-my-tests">React Hooks: What’s going to happen to my tests?</a></li><li><a href="https://kentcdodds.com/blog/common-testing-mistakes">Common Testing Mistakes</a></li><li><a href="https://kentcdodds.com/blog/ui-testing-myths">UI Testing Myths</a></li><li><a href="https://kentcdodds.com/blog/the-merits-of-mocking">The Merits of Mocking</a></li><li><a href="https://kentcdodds.com/blog/react-is-an-implementation-detail">React is an implementation detail</a></li><li><a href="https://kentcdodds.com/blog/eliminate-an-entire-category-of-bugs-with-a-few-simple-tools">Eliminate an entire category of bugs with a few simple tools</a></li><li><a href="https://kentcdodds.com/blog/why-youve-been-bad-about-testing">Why you’ve been bad about testing</a></li><li><a href="https://kentcdodds.com/blog/demystifying-testing">Demystifying Testing</a></li><li><a href="https://kentcdodds.com/blog/confidently-shipping-code">Confidently Shipping Code</a></li><li><a href="https://kentcdodds.com/blog/why-i-never-use-shallow-rendering">Why I Never Use Shallow Rendering</a></li><li><a href="https://kentcdodds.com/blog/test-isolation-with-react">Test Isolation with React</a></li><li><a href="https://kentcdodds.com/blog/but-really-what-is-a-javascript-mock">But really, what is a JavaScript mock?</a></li><li><a href="https://kentcdodds.com/blog/but-really-what-is-a-javascript-test">But really, what is a JavaScript test?</a></li><li><a href="https://kentcdodds.com/blog/effective-snapshot-testing">Effective Snapshot Testing</a></li><li><a href="https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change">Making your UI tests resilient to change</a></li><li><a href="https://kentcdodds.com/blog/write-tests">Write tests. Not too many. Mostly integration.</a></li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://peterhrynkow.com/testing/2019/01/01/testing-atoms-and-molecules.html">Testing Atoms &amp; Molecules</a> | |
by <a href="https://twitter.com/peterhry">Peter Hrynkow</a> is a great blog post about | |
where to focus your testing (using a connected Redux component as an example)</li><li><a href="https://www.javascriptjanuary.com/blog/react-hooks-array-destructuring-fundamentals">React Hooks: Array Destructuring Fundamentals</a> - | |
This may look familiar. It&#x27;s a cross-post of my blog from a while back on the | |
fantastic <a href="https://www.javascriptjanuary.com">JavaScriptJanuary.com</a> by the | |
amazing <a href="https://twitter.com/editingemily">Emily Freeman</a>. Checkout the other | |
posts!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[2018 in Review]]></title> | |
<description><![CDATA[Whether you've had a great 2018 or not, I think it's important to look back and | |
reflect on your accomplishments for the year. You've probably done more than you | |
think you have. In this newsletter, I'm going to share with you some of my | |
professional…]]></description> | |
<link>https://kentcdodds.com/blog/2018-in-review</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/2018-in-review</guid> | |
<pubDate>Mon, 14 Jan 2019 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Whether you&#x27;ve had a great 2018 or not, I think it&#x27;s important to look back and | |
reflect on your accomplishments for the year. You&#x27;ve probably done more than you | |
think you have. In this newsletter, I&#x27;m going to share with you some of my | |
professional (and unprofessional) accomplishments of which I&#x27;m particularly | |
proud and give a few hints as to what I&#x27;m planning for 2019 (which is actually | |
mostly a secret and surprise 😃).</p><blockquote><p>Note: I don&#x27;t want to bother with trying to sort these in any particular | |
order, so... they&#x27;re not in any particular order...</p></blockquote><h2><a href="https://github.com/testing-library/react-testing-library">react-testing-library</a></h2><p>This year I | |
<a href="https://github.com/testing-library/react-testing-library/commit/4f16c6e6b356fae1ad92f59eebeb1a8000f60714">created</a> | |
and | |
<a href="https://kentcdodds.com/blog/introducing-the-react-testing-library">introduced react-testing-library</a>. | |
It has <a href="https://www.npmtrends.com/react-testing-library">grown a lot</a> since | |
then. <a href="https://spectrum.chat/testing-library">The spectrum community</a> has | |
<a href="https://twitter.com/kentcdodds/status/1079523389553790976">over 300 members now</a>. | |
We&#x27;ve <a href="https://twitter.com/Saifadin/status/1079746658882195457">really</a> | |
<a href="https://twitter.com/blankmannow/status/1079358441380954112">had</a> | |
<a href="https://twitter.com/LaurenBeatty13/status/1079489139433762819">a</a> | |
<a href="https://twitter.com/littlekope0903/status/1077045168195452928">whole</a> | |
<a href="https://twitter.com/fatihkupkil/status/1077165153056157699">lot</a> | |
<a href="https://twitter.com/jeanbauerr/status/1076935301166260227">of</a> | |
<a href="https://twitter.com/housecor/status/1074319158362415106">love</a>... | |
<a href="https://twitter.com/BjoernRicks/status/1070596680540672000">like</a>, | |
<a href="https://twitter.com/jaredpalmer/status/1060912456829231104">a</a> | |
<a href="https://twitter.com/jaredpalmer/status/1060348319485095936">lot</a> | |
<a href="https://twitter.com/th__chia/status/1058542488816803840">of</a> | |
<a href="https://twitter.com/antontelesh/status/976364558586667009">sweet</a> | |
<a href="https://twitter.com/skidding/status/1054718393209753600">tweets</a> | |
<a href="https://twitter.com/housecor/status/1047150875502960641">of</a> | |
<a href="https://twitter.com/JeffreyRussom/status/1003996108270555136">appreciation</a> | |
(<a href="https://twitter.com/dan_abramov/status/1078709118863593472">Dan even called it &quot;not bad&quot;</a>). | |
Shout out to | |
<a href="https://twitter.com/ryanflorence/status/975424602699530240">Ryan Florence for the name</a> | |
and <a href="https://twitter.com/thymikee/status/1050372496971706368">Michał Pierzchała</a> | |
for making | |
<a href="https://github.com/callstack/react-native-testing-library">react-native-testing-library</a>.</p><p>I&#x27;m super proud of what we&#x27;ve accomplished here. The | |
<a href="https://github.com/testing-library/react-testing-library/blob/master/README.md#contributors">react-testing-library all contributors table</a> | |
lists 63 awesome people, and the | |
<a href="https://github.com/testing-library/dom-testing-library/blob/master/README.md#contributors">dom-testing-library all contributors table</a> | |
lists 46 (many repeats, but not all). These people are amazing and I really | |
appreciate what they&#x27;ve done. I don&#x27;t want to leave anyone out, but I would like | |
to give a special shout out to these folks: <a href="https://twitter.com/Gpx">Giorgio</a>, | |
<a href="https://twitter.com/TensorNo">Alex Krolick</a>, | |
<a href="https://twitter.com/sompylasar">Ivan Babak</a>, | |
<a href="https://twitter.com/gnapse">Ernesto García</a>, and | |
<a href="https://twitter.com/lgandecki">Łukasz Gandecki</a>.</p><p>Creating this open source software and the community of awesome people that has | |
been built around it is probably one of my finest accomplishments of 2019. If | |
you&#x27;d like to hear more about how this software came to be, you can watch | |
<a href="https://youtu.be/nw-GFMW8LSQ">S05E12 Modern Web Podcast - Testing</a> or listen to | |
<a href="https://devchat.tv/react-round-up/rru-043-testing-react-apps-without-testing-implementation-details-with-kent-c-dodds">RRU 043: Testing React Apps Without Testing Implementation Details with Kent C. Dodds</a>.</p><h2><a href="https://nanowrimo.org">National Novel Writing Month</a></h2><p>Of all non-professional things I&#x27;ve done this year, I&#x27;m most proud of this one. | |
If you didn&#x27;t know, I wrote a 50,000 word novel in the month of November for | |
<a href="https://nanowrimo.org">NaNoWriMo</a>. This was the first year I tried and I WON!</p><p>Back in August, I decided I wanted to become better at storytelling because my | |
kids are always asking me to tell them stories and I wasn&#x27;t very good at it. | |
Something reminded me of my friend in college who had written a novel in one | |
month for NaNoWriMo. I had been listening to a LOT of | |
<a href="https://brandonsanderson.com">Brandon Sanderson</a> recently (more on this later) | |
and decided to try writing my own Sanderson-style fantasy novel.</p><p>I spent the next few months preparing. I listened to | |
<a href="https://storygrid.simplecast.fm">the entire story grid podcast</a>, four seasons | |
of <a href="https://writingexcuses.com">the writing excuses podcast</a> (they have a lot of | |
seasons), I talked through the story, characters, magic, and more with my wife | |
and she gave me some brilliant ideas, I made | |
<a href="https://workflowy.com/s/--B.0EZBOxYZWM">an in depth outline and took notes</a> | |
(<a href="https://workflowy.com/invite/a9f7933.lnx">workflowy is awesome by the way</a>), | |
and helped inspire the creation of <a href="https://kcd.im/dww">DevsWhoWrite discord</a> | |
where I joined several other awesome devs who... well... write 😉 (feel free to | |
join us!).</p><p>The end result is <a href="https://kcd.im/shurlan">Shurlan</a>. I&#x27;m super proud of it and | |
still working on editing to prepare it for publication! Here&#x27;s a little summary:</p><blockquote><p>The Immortal family has ruled Shurlan for thousands of years. Thanks to their | |
wisdom, the perfect society has been formed and peace and plenty graces | |
Shurlan. But when the food allotments start to dwindle, a rebellion begins, | |
and only those with secret magic abilities can stop them.</p><p>Kyana, an extremely skilled gravity displacer (known as a drifter) is chosen | |
by Lord Talmar of the Immortal family to do the impossible task of recruiting | |
non-displacers and training them to learn a displacement skill. They need to | |
find and stop the rebellion before they steal the harvest and their families | |
starve.</p></blockquote><p>This is a <a href="https://en.wikipedia.org/wiki/Hard_fantasy">hard fantasy</a> novel. That | |
means that the magic system and world are intended to be rational and knowable | |
(it&#x27;s also really cool). It&#x27;s also | |
<a href="https://en.wikipedia.org/wiki/Juvenile_fantasy">juvenile fantasy</a>, which means | |
it&#x27;s an enjoyable read for adults and kids alike (think Harry Potter).</p><p>What I think makes this book special is the message I&#x27;m trying to communicate, | |
the world I&#x27;ve created, the characters, and the magic system.</p><p>I&#x27;ve totally immersed myself in this world and the world of writing. I&#x27;m | |
planning on attending writing conferences this year to do some networking and | |
improving my craft. I&#x27;m looking forward to November 2019 when I&#x27;ll write book 2 | |
in the Shurlan series. I have the concept for 4 series (3 books each) in this | |
world. Feeling determined :)</p><h2><a href="https://testingjavascript.com">TestingJavaScript.com</a></h2><p>This was a HUGE effort by me and the good folks at | |
<a href="https://egghead.io">egghead.io</a>. It&#x27;s the equivalent of 7 full sized egghead.io | |
courses + 9 podcast episodes. It was a TON of work and people LOVE it. So I&#x27;m | |
really happy with this.</p><h2><a href="https://kentcdodds.com/blog/tools-without-config">paypal-scripts</a></h2><p>I started this project in August of 2017, but most of the work was done in 2018. | |
It&#x27;s basically a single tool that consolidates a bunch of other tools common to | |
PayPal projects (both applications and reusable modules published to our | |
internal npm registry). Think of it like create-react-app&#x27;s react-scripts, or | |
ember-cli, or angular-cli. But it does a lot more than just the build/tests. | |
Here are all the available scripts as of today:</p><pre><code>build | |
clean | |
dev | |
format | |
gh-pages | |
lint | |
pre-commit | |
release | |
remark | |
test | |
typecheck | |
validate | |
</code></pre><p>It would take another blog post to explain what these all do and how it does | |
them. But suffice it to say, this was a herculean effort that took most of my | |
time at work this year.</p><p>One huge accomplishment around paypal-scripts was in the last month I decided to | |
adopt TypeScript (that&#x27;s a blog post for another time) and I was heads down | |
updating all the tools to work with <code>.ts</code> and <code>.tsx</code> files.</p><p>What makes this project such a big deal is that it&#x27;s very soon to be the basis | |
of the default template project at PayPal we have called the &quot;sample-app.&quot; Every | |
new app started at PayPal is basically a fork of this &quot;sample-app.&quot; So because | |
of the work that I and others have done, every new app at PayPal will be written | |
in TypeScript, use Jest, React, react-testing-library, emotion, webpack, babel, | |
prettier, and eslint. And what makes it better is those apps wont have to worry | |
about keeping those tools up to date saving dozens of developer hours a year PER | |
PROJECT. I&#x27;m really proud of this accomplishment.</p><blockquote><p>(Before you ask, this will not be open sourced, it&#x27;s too PayPal specific, but | |
you might be interested in forking | |
<a href="https://github.com/kentcdodds/kcd-scripts">kcd-scripts</a>).</p></blockquote><h2><a href="https://paypal.me">PayPal.me</a> rewrite</h2><p>At the beginning of the year, PayPal decided to make some significant changes to | |
parts of the experience and that had a big impact on how | |
<a href="https://paypal.me">PayPal.me</a> was supposed to work. The original implementation | |
would be hard to upgrade incrementally and the app is pretty simple anyway, so | |
we decided to do a complete rewrite. We used paypal-scripts for all the tooling | |
(it was the first major production app to do so) and we were able to get the | |
tooling side of things done without much configuration or wiring together tools | |
of any kind (I had to make quite a few adjustments to paypal-scripts though 😅 | |
but it&#x27;s good now I promise). That was an awesome experience for paypal-scripts | |
and I&#x27;m really excited to see the experience for everyone else at PayPal as | |
adoption increases.</p><p>Building the app was great. We ended up using | |
<a href="https://github.com/jamiebuilds/unstated">unstated</a> for state management and | |
we&#x27;re pretty happy with it. We used | |
<a href="https://github.com/paypal/glamorous">glamorous</a> (decided that before deciding | |
to deprecate glamorous, we&#x27;ll be moving to emotion eventually). The backend was | |
GraphQL (huge thanks to <a href="https://twitter.com/arnab0831">Arnab Banik</a> who did | |
most of the work there). It&#x27;s a pretty simple app, so we&#x27;re not using Apollo or | |
anything, just | |
<a href="https://www.npmjs.com/package/graphql-request"><code>graphql-request</code></a> and that | |
worked well for our needs.</p><p>Anyway, really happy with how that turned out :)</p><h2>Open Source</h2><p>Aside from creating react-testing-library (and dom-testing-library) this year, | |
there are a few other accomplishments I had this year/useless numbers in the | |
realm of Open Source that I&#x27;d like to mention. Here are a few numbers:</p><ul><li>Hit 500 repos (most of them are nonsense, I do have over 100 published npm | |
packages though, so there&#x27;s that).</li><li><a href="https://github.com/facebook/create-react-app/pull/3675">Added <code>babel-plugin-macros</code> to <code>create-react-app</code></a> | |
and it was <a href="https://github.com/gatsbyjs/gatsby/pull/7129">added to gatsby</a>. | |
There are | |
<a href="https://www.npmjs.com/browse/depended/babel-plugin-macros">over 200 dependents published on npm</a>!</li><li><a href="https://github.com/search?q=created%3A%3E2018-01-01+is%3Aissue+author%3Akentcdodds&amp;type=Issues">I opened 134 issues</a></li><li><a href="https://github.com/search?q=created%3A%3E2018-01-01+is%3Apr+author%3Akentcdodds+-user%3Akentcdodds">I created 86 PRs</a> | |
(<a href="https://github.com/search?utf8=%E2%9C%93&amp;q=created%3A%3E2018-01-01+is%3Apr+author%3Akentcdodds+-user%3Akentcdodds+is%3Amerged&amp;ref=simplesearch">73 were merged</a>)</li><li><a href="https://github.com/search?q=created%3A%3E2018-01-01+is%3Apr+reviewed-by%3Akentcdodds&amp;type=Issues">I reviewed 802 PRs</a>... | |
WOW. 🤯</li><li>I <a href="https://github.com/paypal/glamorous/issues/419">deprecated glamorous</a> in | |
favor of <a href="https://emotion.sh">emotion.sh</a></li><li>Published v2 and v3 of <a href="https://github.com/downshift-js/downshift">downshift</a>.</li><li>There are now | |
<a href="https://github.com/search?q=.all-contributorsrc+in%3Apath&amp;type=Code&amp;utf8=%E2%9C%93">over 2k <code>.all-contributorsrc</code> files on GitHub</a> | |
😱</li><li>All in all, | |
<a href="https://github.com/kentcdodds">I had 2,337 GitHub contributions in the last year</a>.</li></ul><h2><a href="https://kcd.im/youtube">My YouTube Channel</a></h2><p>This was the year I decided to do week-daily livestreams on | |
<a href="https://kcd.im/youtube">my YouTube channel</a>: | |
<a href="https://kcd.im/devtips">Dev Tips with Kent</a>. I&#x27;ve got over 100 videos on the | |
dev tips playlist. Some of them are longer than others, some of them are more | |
coherent than others, but altogether they represent a great resource for people | |
to learn things from JavaScript to testing to React to Babel to more and more :)</p><p>I also have a playlist called | |
<a href="https://youtube.com/playlist?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Talks and workshops from Kent C. Dodds</a> | |
with over 60 videos. More on this later.</p><p>Altogether, this year I&#x27;ve really been pushing content onto YouTube and | |
<a href="https://twitter.com/kentcdodds/status/1073589861054070784">hit over 10k subscribers</a>. | |
My videos got over 260k views this year (I am of course excluding | |
<a href="https://youtu.be/t6q5_7fVjEg">my smiley face video</a> | |
<a href="https://github.com/kentcdodds/ama/issues/290">which was stolen</a> and has 3.7 | |
million views now).</p><h2>Conferences</h2><p>I gave a lot of talks this year, you can find all the available recordings on | |
<a href="https://youtube.com/playlist?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">my YouTube playlist</a>. | |
I spoke at 7 conferences and several meetups. I was especially proud to deliver | |
<a href="https://youtu.be/M9X2qGddHkU?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">my first keynote</a> | |
at <a href="https://infinite.red/ChainReactConf">Chain React</a> which was awesome.</p><p>I gave | |
<a href="https://youtu.be/AiJ8tRRH0f8?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">a similar talk</a> | |
at <a href="https://www.reactrally.com">React Rally</a> which is my favorite conference and | |
has been a dream of mine since year one.</p><p>I try to avoid traveling to conferences, so most of the conferences I spoke at | |
were either local or remote. I did travel to San Antonio for | |
<a href="https://www.assertjs.com">Assert.js</a> and Portland for | |
<a href="https://infinite.red/ChainReactConf">Chain React</a>. The only other travel I did | |
this year was to PayPal offices to give workshops/trainings in San Jose and | |
Austin, TX.</p><h2>Other</h2><p>Here are some other interesting facts/accomplishments from 2018:</p><ul><li><a href="https://twitter.com/ryanflorence/status/1062114045417873408">I read the Book of Mormon</a> | |
again.</li><li><a href="https://twitter.com/kentcdodds">Twitter</a>: I went from | |
<a href="https://twitter.com/kentcdodds/status/955863656788643840">25k followers</a> to | |
almost 50k.</li><li><a href="https://kcd.im/news">Newsletter</a>: I went from | |
<a href="https://twitter.com/kentcdodds/status/973023708473278465">less than 5k subscribers</a> | |
to over 10k.</li><li><a href="https://brandonsanderson.com">Brandon Sanderson</a>: I listened to almost 30 | |
DAYS worth of time of Brandon Sanderson fantasy novels in 2018. I listen at | |
2.9x speed, so it amounts to ~10 days of time, but still. I&#x27;ve filled my ears | |
with his words this year!</li><li>I posted 62 new blog posts on <a href="https://kentcdodds.com/blog">my blog</a> on subjects around JavaScript, | |
Testing, React, as well as career advice.</li><li>In 2018, I spent most of my course time on testingjavascript.com, but I did | |
manage to make a complete update to | |
<a href="https://kcd.im/advanced-react">Advanced React Component Patterns</a> and make | |
<a href="https://egghead.io/courses/simplify-react-apps-with-react-hooks">Simplify React Apps with React Hooks</a> | |
as well as <a href="https://kcd.im/hooks-and-suspense">Hooks &amp; Suspense Playlist</a>. I | |
don&#x27;t have easy access to stats, but in last night&#x27;s egghead stats email | |
people watched ~26 hours of my content yesterday and that&#x27;s pretty typical. I | |
think that&#x27;s pretty neat!</li><li>Back in April, I made a trip to Minneapolis to record workshops for | |
<a href="https://kcd.im/fem">Frontend Masters</a>. We made three (almost four) courses | |
out of that trip: | |
<a href="https://frontendmasters.com/courses/advanced-react-patterns">Advanced React Patterns</a>, | |
<a href="https://frontendmasters.com/courses/testing-react">Testing React Applications, v2</a>, | |
and | |
<a href="https://frontendmasters.com/courses/testing-practices-principles">JavaScript Testing Practices and Principles</a>. | |
I&#x27;m really happy about how those turned out!</li><li>For the first part of the year, | |
<a href="https://twitter.com/ryanflorence">Ryan Florence</a> made | |
<a href="https://twitter.com/workshop_me">Workshop.me</a>. I gave 5 workshops (two on | |
testing, two on advanced react, and one on beginning react) through | |
workshop.me and it was awesome (though short-lived).</li><li>I gave 4 other workshops this year all of which you can find on | |
<a href="https://youtube.com/playlist?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">my YouTube playlist</a>.</li></ul><h2>What&#x27;s next in 2019</h2><p>I&#x27;m really excited about plans I have for 2019. I&#x27;m not going to give away all | |
my surprises, but here are a few things:</p><ul><li>I&#x27;m turning my <a href="https://kcd.im/3-mins">3 minutes with Kent podcast</a> into a | |
week-daily Q&amp;A. Give it a look, subscribe, and ask your own question on my | |
AMA: <a href="https://kcd.im/ama">kcd.im/ama</a></li><li>I&#x27;m revamping <a href="https://kentcdodds.com">my website</a> using Gatsby and a bunch of | |
other cool tools. I&#x27;ll definitely blog about it. I&#x27;m actually kinda excited | |
about it :) I&#x27;ll also be moving my blog from Medium to my website with gatsby | |
and hopefully automate more parts of this newsletter stuff :)</li><li>Remember TestingJavaScript.com? How would you like something like that for | |
learning React? But even bigger? From total beginner to JS to total React | |
professional? This is going to be enormous.</li><li>I avoid traveling as much as possible, but I want to give more workshops this | |
year than I gave last year. So I&#x27;m going to do online workshops. There are | |
<a href="https://twitter.com/kentcdodds/status/1022675523154014209">a lot of benefits to remote workshops</a>. | |
I&#x27;ve done it and I know how to make up for the difficulties of not being there | |
in person. You&#x27;re going to want to take advantage of these!</li><li>At PayPal, I&#x27;m working on a component library. I imagine there are some | |
opportunities to open source the generic stuff (HOOKS!)</li><li>I&#x27;m scheduled to speak and give workshops at | |
<a href="https://react.amsterdam">React Amsterdam</a> in April! See you there? I have at | |
least one other conference that I&#x27;ll probably be speaking at soon as well. I | |
expect I&#x27;ll be speaking a lot around here in Utah as well.</li><li>I have about 15 fantasy novels in mind for the Shurlan Universe. I&#x27;m | |
definitely planning on writing one each year in November and working | |
throughout the year to get each published. I&#x27;m feeling pretty optimistic and | |
motivated about this whole novel writing stuff :)</li></ul><h2>Conclusion</h2><p>I hope you take the opportunity to look back at your year and see what you&#x27;ve | |
accomplished. Then make some goals to become even better than you are now.</p><blockquote><p>&quot;There is nothing noble in being superior to your fellow man; true nobility is | |
being superior to your former self.&quot; ― Ernest Hemingway</p></blockquote><p>I wish you the very best and happiest New Year!</p><hr/><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://devchat.tv/react-round-up/rru-043-testing-react-apps-without-testing-implementation-details-with-kent-c-dodds">RRU 043: Testing React Apps Without Testing Implementation Details with Kent C. Dodds</a> - | |
The ReactRoundUp podcast had me on to talk about testing!</li><li><a href="https://mhme-blog.now.sh/2018/december/testing-software.html">Testing Software</a> - | |
Great insights here by <a href="https://mobile.twitter.com/immatthamlin">Matt Hamlin</a></li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/2018-in-review">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React Hooks: Array Destructuring Fundamentals]]></title> | |
<description><![CDATA[This is the first example on the https://reactjs.org/hooks documentation: That const [count, setCount] = useState(0); is the line we're going to be | |
talking about today. The syntax here is called "array destructuring" and it was | |
introduced into…]]></description> | |
<link>https://kentcdodds.com/blog/react-hooks-array-destructuring-fundamentals</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/react-hooks-array-destructuring-fundamentals</guid> | |
<pubDate>Mon, 31 Dec 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>This is the first example on the <a href="https://reactjs.org/hooks">https://reactjs.org/hooks</a> documentation:</p><pre><code class="language-javascript">import {useState} from &#x27;react&#x27; | |
function Example() { | |
// Declare a new state variable, which we&#x27;ll call &quot;count&quot; | |
const [count, setCount] = useState(0) | |
return ( | |
&lt;div&gt; | |
&lt;p&gt;You clicked {count} times&lt;/p&gt; | |
&lt;button onClick={() =&gt; setCount(count + 1)}&gt;Click me&lt;/button&gt; | |
&lt;/div&gt; | |
) | |
} | |
</code></pre><p>That <code>const [count, setCount] = useState(0);</code> is the line we&#x27;re going to be | |
talking about today. The syntax here is called &quot;array destructuring&quot; and it was | |
introduced into JavaScript in the infamous | |
(<a href="https://youtu.be/0b6_i_eSgR8">more than famous</a>) | |
<a href="https://github.com/lukehoban/es6features">ES6 release</a>.</p><p>I&#x27;m a firm believer that:</p><blockquote><p><a href="https://twitter.com/kentcdodds/status/1074724545003581440">The better you understand an abstraction, the more effective you will be at using it.</a> | |
– me, literally right when I wrote this...</p></blockquote><p>So when I see syntax that I&#x27;m unfamiliar with, I like to read about it and | |
understand how it works with the rest of the language. The problem is that it | |
can be difficult to &quot;Google&quot; syntax. Seriously... Try Googling the syntax itself | |
as if you didn&#x27;t know that it&#x27;s called &quot;destructuring.&quot; Pretty tough! So here&#x27;s | |
my trick. I go to <a href="https://astexplorer.net">astexplorer.net</a> and paste in | |
<a href="https://astexplorer.net/#/gist/c6480f8f68861065fd0f91871540a21f/1dfec03fcab33fb1c4b63e9821c4fc69bcf15973">the code</a> | |
that I don&#x27;t understand:</p><p><img src="https://i.imgur.com/oZGLSfr.png" alt="ASTExplorer.net with the code showing ArrayPattern"/></p><p>Cool! Babel calls that an &quot;ArrayPattern.&quot; So let&#x27;s go ahead and Google for that. | |
We&#x27;ll search for &quot;site:<a href="https://developer.mozilla.org">https://developer.mozilla.org</a> array pattern&quot; (that way | |
Google only returns results for articles on MDN which is a terrific resource on | |
learning everything there is to know about JavaScript).</p><p>Sweet, the first result takes us to | |
<a href="https://mdn.io/destructuring">&quot;Destructuring assignment&quot;</a> where we can learn | |
all about this feature (I guess you can read that instead of continuing here if | |
you want to 😅).</p><p>Often syntax like this is what we call &quot;syntactic sugar&quot; for other features. | |
Here&#x27;s what wikipedia says about | |
<a href="https://en.wikipedia.org/wiki/Syntactic_sugar">syntactic sugar</a>:</p><blockquote><p>In | |
<a href="https://en.wikipedia.org/wiki/Computer_science" title="Computer science">computer science</a>, | |
<strong>syntactic sugar</strong> is syntax within a | |
<a href="https://en.wikipedia.org/wiki/Programming_language" title="Programming language">programming language</a> | |
that is designed to make things easier to read or to express. It makes the | |
language &quot;sweeter&quot; for human use: things can be expressed more clearly, more | |
concisely, or in an alternative style that some may prefer.</p></blockquote><p>Ok, so basically it means that there are common patterns or ways to write code | |
in a given language, so the language makes a syntax feature to make that pattern | |
require less code or more expressive. With this in mind, when I&#x27;m learning new | |
syntax, I like to &quot;de-sugar&quot; the syntax to see what it would look like if we | |
didn&#x27;t have that feature.</p><p>Luckily for us, we have Babel and TypeScript which can compile this newer syntax | |
into something older browsers can support (and presumably to something we may be | |
more familiar with). So my next step is to go to | |
<a href="https://babeljs.io/repl">the online babel REPL</a> and paste in the code. Here&#x27;s | |
what the result looks like:</p><pre><code class="language-javascript">&#x27;use strict&#x27; | |
var _slicedToArray = (function() { | |
function sliceIterator(arr, i) { | |
var _arr = [] | |
var _n = true | |
var _d = false | |
var _e = undefined | |
try { | |
for ( | |
var _i = arr[Symbol.iterator](), _s; | |
!(_n = (_s = _i.next()).done); | |
_n = true | |
) { | |
_arr.push(_s.value) | |
if (i &amp;&amp; _arr.length === i) break | |
} | |
} catch (err) { | |
_d = true | |
_e = err | |
} finally { | |
try { | |
if (!_n &amp;&amp; _i[&#x27;return&#x27;]) _i[&#x27;return&#x27;]() | |
} finally { | |
if (_d) throw _e | |
} | |
} | |
return _arr | |
} | |
return function(arr, i) { | |
if (Array.isArray(arr)) { | |
return arr | |
} else if (Symbol.iterator in Object(arr)) { | |
return sliceIterator(arr, i) | |
} else { | |
throw new TypeError( | |
&#x27;Invalid attempt to destructure non-iterable instance&#x27;, | |
) | |
} | |
} | |
})() | |
// const [count, setCount] = useState(0); | |
var _useState = useState(0), | |
_useState2 = _slicedToArray(_useState, 2), | |
count = _useState2[0], | |
setCount = _useState2[1] | |
</code></pre><p>😬 YIKES! Hmmm... Ok, so sometimes Babel uses utilities which both make it more | |
compliant to the specification, but also can make the code a little harder to | |
understand. Luckily, there&#x27;s an option on the Babel Repl&#x27;s &quot;Env Preset&quot; called | |
&quot;Loose&quot; which will simplify this output considerably:</p><pre><code class="language-javascript">// const [count, setCount] = useState(0); | |
var _useState = useState(0), | |
count = _useState[0], | |
setCount = _useState[1] | |
</code></pre><p>😌 Phew, that&#x27;s better. Ok, so what&#x27;s going on here. Babel&#x27;s taking our one line | |
and rather than using the Array Pattern thing, it&#x27;s assigning the return value | |
of <code>useState</code> to a variable called <code>_useState</code>. Then it&#x27;s treating <code>_useState</code> | |
as an array and it assigns <code>count</code> to the first item in the array and <code>setCount</code> | |
to the second one.</p><p>Let&#x27;s play around with this a little bit to explore the syntax:</p><p>Can I call the values whatever I want?</p><pre><code class="language-javascript">// const [whateverIWant, reallyICanChooseWhatItIsCalled] = useState(0); | |
var _useState = useState(0), | |
whateverIWant = _useState[0], | |
reallyICanChooseWhatItIsCalled = _useState[1] | |
</code></pre><p>Can I add more elements?</p><pre><code class="language-javascript">// const [count, setCount, somethingElse] = useState(0); | |
var _useState = useState(0), | |
count = _useState[0], | |
setCount = _useState[1], | |
somethingElse = _useState[2] | |
</code></pre><p>Can I pull out fewer?</p><pre><code class="language-javascript">// const [count] = useState(0); | |
var _useState = useState(0), | |
count = _useState[0] | |
</code></pre><p>Can I skip one?</p><pre><code class="language-javascript">// const [, setCount] = useState(0); | |
var _useState = useState(0), | |
setCount = _useState[1] | |
</code></pre><p>Can I skip more?</p><pre><code class="language-javascript">// const [,,, wow,, neat] = useState(0); | |
var _useState = useState(0), | |
wow = _useState[3], | |
neat = _useState[5] | |
</code></pre><p>I saw someone put a weird <code>=</code> sign in there, what does that do?</p><pre><code class="language-javascript">// const [count = 3, setCount] = useState(0); | |
var _useState = useState(0), | |
_useState$ = _useState[0], | |
count = _useState$ === undefined ? 3 : _useState$, | |
setCount = _useState[1] | |
</code></pre><p>Oooh, fancy, so if the first element of the array is undefined, then we&#x27;ll set | |
<code>count</code> to <code>3</code> instead. Default values! Sweet.</p><blockquote><p>Note: most of the things above you would never need to do with <code>useState</code> | |
because we can always rely on <code>useState</code> returning an array of two elements! | |
We&#x27;ll look at that more next.</p></blockquote><p>Ok cool, so this helps us understand what&#x27;s actually going on. There&#x27;s nothing | |
React-specific about this syntax. It&#x27;s built-into the JavaScript specification, | |
and React&#x27;s <code>useState</code> hook is leveraging it as a mechanism for an ergonomic API | |
that allows you to get two values out of a single function call. Neat!</p><p>Ok, so what does <code>useState</code> actually <em>do</em> then? What is it really returning? It | |
must be returning an array for us to be doing the array destructuring like this | |
right? Cool, let&#x27;s check that out.</p><p>One thing that&#x27;s interesting is that the implementation of <code>useState</code> exists | |
within <code>react-dom</code> rather than <code>react</code>. I know, that may be confusing because we | |
import <code>useState</code> from the <code>react</code> package, but it actually just delegates to | |
the current renderer (which is <code>react-dom</code> in our situation here). In fact, | |
<a href="https://overreacted.io/how-does-setstate-know-what-to-do"><code>setState</code> is the same way</a>!</p><p>Another interesting thing about <code>useState</code> is that the implementation in | |
<code>react-dom</code> is just a few lines:</p><pre><code class="language-javascript">function useState(initialState) { | |
return useReducer( | |
basicStateReducer, | |
// useReducer has a special case to support lazy useState initializers | |
initialState, | |
) | |
} | |
</code></pre><p>😱 it&#x27;s actually just a hook that&#x27;s using the <code>useReducer</code> hook! Ok, but what is | |
that <code>basicStateReducer</code> thing huh?</p><pre><code class="language-javascript">function basicStateReducer(state, action) { | |
return typeof action === &#x27;function&#x27; ? action(state) : action | |
} | |
</code></pre><p>Ok, interesting, so <code>useReducer</code> is actually | |
<a href="https://github.com/facebook/react/blob/f1bf281605444b342f4c37718092accbe3f98702/packages/react-reconciler/src/ReactFiberHooks.js#L471">over 100 lines of code</a>, | |
so let&#x27;s just look at what <code>useReducer</code> | |
<a href="https://github.com/facebook/react/blob/f1bf281605444b342f4c37718092accbe3f98702/packages/react-reconciler/src/ReactFiberHooks.js#L383">returns</a>:</p><pre><code class="language-javascript">return [newState, dispatch] | |
</code></pre><p>See! It&#x27;s an array! So when we call <code>useState</code>, it returns a call to | |
<code>useReducer</code> which will return an array of two values. This allows us to do the | |
array destructuring that we want so instead of writing:</p><pre><code class="language-javascript">const stateAndUpdater = useState(0) | |
const count = stateAndUpdater[0] | |
const setCount = stateAndUpdater[1] | |
</code></pre><p>We can write:</p><pre><code class="language-javascript">const [count, setCount] = useState(0) | |
</code></pre><p>Nice!</p><h2>Conclusion</h2><p>I hope you found this one helpful! Even if you already are very familiar with | |
destructing syntax, the process of learning new syntax I show above has been | |
helpful to me as recently as Friday when I was playing around with TypeScript. | |
Seeing syntax that I&#x27;m not familiar with and learning new things is something | |
that I&#x27;ll never get tired of in this industry! And learning the fundamentals | |
behind these bits of syntax will make you more effective at using them. I should | |
mention also that there are more things you can do with destructuring and if | |
you&#x27;re interested there&#x27;s | |
<a href="https://youtu.be/t3R3R7UyN2Y?t=1h07m01s&amp;list=PLV5CVI1eNcJgUA2ziIML3-7sMbS7utie5">a section about destructuring in my ES6 workshop</a> | |
that&#x27;s available completely free on | |
<a href="https://kcd.im/youtube">my YouTube channel</a>. Good luck!</p><hr/><p><strong>Learn more about React from me</strong>:</p><ul><li><a href="https://kcd.im/beginner-react">The Beginner&#x27;s Guide to React</a> - Absolute | |
fundamentals of React</li><li><a href="https://kcd.im/hooks-and-suspense">React Hooks and Suspense</a> - A great primer | |
on Hooks and Suspense</li><li><a href="https://kcd.im/refactor-react">Simplify React Apps with React Hooks</a> - Let&#x27;s | |
take some real-world class components and refactor them to function components | |
with hooks.</li><li><a href="https://kcd.im/advanced-react">Advanced React Component Patterns</a> - Amazing | |
patterns to make your components more reusable, flexible, and simple all at | |
once. (Also on | |
<a href="https://frontendmasters.com/courses/advanced-react-patterns">Frontend Masters</a>).</li><li><a href="https://kcd.im/youtube">My YouTube channel</a> is also full of content about | |
<a href="https://youtube.com/user/kentdoddsfamily/search?query=react">React</a> that | |
you&#x27;d probably enjoy (including | |
<a href="https://youtube.com/playlist?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">workshops, talks</a>, | |
and <a href="https://kcd.im/devtips">livestreams</a>).</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://devhubapp.com">DevHub</a> - TweetDeck for GitHub by | |
<a href="https://twitter.com/brunolemos">Bruno Lemos</a>. The project itself is | |
<a href="https://github.com/devhubapp/devhub">open source</a> and pretty impressive. It&#x27;s | |
<a href="https://twitter.com/brunolemos/status/1072871009651384320">&quot;a production app using React Hooks, React Native Web with a 95%+ code sharing between web and mobile, CRA, TypeScript, Yarn Workspaces and Redux&quot;</a></li><li><a href="https://www.modernjsbyexample.net">Modern Javascript by Example</a> - A free | |
(donations welcome) open source book by | |
<a href="https://twitter.com/MrBenJ5">Ben Junya</a></li><li><a href="https://www.byteconf.com/blog/what-i-use-kent-c-dodds">What I Use: Kent C. Dodds</a> - | |
<a href="https://twitter.com/byteconf">Byteconf</a> is starting something new by talking | |
to their favorite folks in the software dev world to find out how they do | |
their best work. I was the first one!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/react-hooks-array-destructuring-fundamentals">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React Hooks: What's going to happen to my tests?]]></title> | |
<description><![CDATA[Current Available Translations: Korean One of the most common questions I hear about the upcoming React Hooks feature | |
is regarding testing. And I can understand the concern when your tests look like | |
this: That enzyme test works when Accordion is a…]]></description> | |
<link>https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-my-tests</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-my-tests</guid> | |
<pubDate>Mon, 24 Dec 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Current Available Translations:</p><ul><li><a href="https://edykim.com/ko/post/react-hooks-whats-going-to-happen-to-my-tests">Korean</a></li></ul><p>One of the most common questions I hear about the upcoming React Hooks feature | |
is regarding testing. And I can understand the concern when your tests look like | |
this:</p><pre><code class="language-jsx">// borrowed from a previous blog post: | |
// https://kcd.im/implementation-details | |
test(&#x27;setOpenIndex sets the open index state properly&#x27;, () =&gt; { | |
const wrapper = mount(&lt;Accordion items={[]} /&gt;) | |
expect(wrapper.state(&#x27;openIndex&#x27;)).toBe(0) | |
wrapper.instance().setOpenIndex(1) | |
expect(wrapper.state(&#x27;openIndex&#x27;)).toBe(1) | |
}) | |
</code></pre><p>That enzyme test works when <code>Accordion</code> is a class component where the | |
<code>instance</code> actually exists, but there&#x27;s no concept of a component &quot;instance&quot; | |
when your components are function components. So doing things like <code>.instance()</code> | |
or <code>.state()</code> wont work when you refactor your components from class components | |
with state/lifecycles to function components with hooks.</p><p>So if you were to refactor the <code>Accordion</code> component to a function component, | |
those tests would break. So what can we do to make sure that our codebase is | |
ready for hooks refactoring without having to either throw away our tests or | |
rewrite them? You can start by avoiding enzyme APIs that reference the component | |
instance like the test above. You can read more about this in | |
<a href="https://kcd.im/imp-deets">my &quot;implementation details&quot; blog post</a>.</p><p>Let&#x27;s look at a simpler example of a class component. My favorite example is a | |
<code>&lt;Counter /&gt;</code> component:</p><pre><code class="language-jsx">// counter.js | |
import React from &#x27;react&#x27; | |
class Counter extends React.Component { | |
state = {count: 0} | |
increment = () =&gt; this.setState(({count}) =&gt; ({count: count + 1})) | |
render() { | |
return &lt;button onClick={this.increment}&gt;{this.state.count}&lt;/button&gt; | |
} | |
} | |
export default Counter | |
</code></pre><p>Now let&#x27;s see how we could test it in a way that&#x27;s ready for refactoring it to | |
use hooks:</p><pre><code class="language-jsx">// __tests__/counter.js | |
import React from &#x27;react&#x27; | |
import &#x27;react-testing-library/cleanup-after-each&#x27; | |
import {render, fireEvent} from &#x27;react-testing-library&#x27; | |
import Counter from &#x27;../counter.js&#x27; | |
test(&#x27;counter increments the count&#x27;, () =&gt; { | |
const {container} = render(&lt;Counter /&gt;) | |
const button = container.firstChild | |
expect(button.textContent).toBe(&#x27;0&#x27;) | |
fireEvent.click(button) | |
expect(button.textContent).toBe(&#x27;1&#x27;) | |
}) | |
</code></pre><p>That test will pass. Now, let&#x27;s refactor this to a hooks version of the same | |
component:</p><pre><code class="language-jsx">// counter.js | |
import React from &#x27;react&#x27; | |
function Counter() { | |
const [count, setCount] = useState(0) | |
const incrementCount = () =&gt; setCount(c =&gt; c + 1) | |
return &lt;button onClick={incrementCount}&gt;{count}&lt;/button&gt; | |
} | |
export default Counter | |
</code></pre><p>Guess what! Because our tests avoided implementation details, our hooks are | |
passing! How neat is that!? :)</p><h3>useEffect is not componentDidMount + componentDidUpdate + componentWillUnmount</h3><p>Another thing to consider is the <code>useEffect</code> hook because it actually is a | |
little unique/special/different/awesome. When you&#x27;re refactoring from class | |
components to hooks, you&#x27;ll typically move the logic from <code>componentDidMount</code>, | |
<code>componentDidUpdate</code>, and <code>componentWillUnmount</code>to one or more <code>useEffect</code> | |
callbacks (depending on the number of concerns your component has in those | |
lifecycles). But this is actually <em>not</em> a refactor. Let&#x27;s get a quick review of | |
what a &quot;refactor&quot; actually is.</p><p>When you refactor code, you&#x27;re making changes to the implementation without | |
making user-observable changes. | |
<a href="https://en.wikipedia.org/wiki/Code_refactoring">Here&#x27;s what wikipedia says about &quot;code refactoring&quot;</a>:</p><blockquote><p><strong>Code refactoring</strong> is the process of restructuring existing computer | |
code — changing the | |
<a href="https://en.wikipedia.org/wiki/Decomposition_%28computer_science%29" title="Decomposition (computer science)">factoring</a> | |
without changing its external behavior.</p></blockquote><p>Ok, let&#x27;s try that idea out with an example:</p><pre><code class="language-js">const sum = (a, b) =&gt; a + b | |
</code></pre><p>Here&#x27;s a refactor of this function:</p><pre><code class="language-js">const sum = (a, b) =&gt; b + a | |
</code></pre><p>It still works exactly the same, but the implementation itself is a little | |
different. Fundamentally that&#x27;s what a &quot;refactor&quot; is. Ok, now, here&#x27;s what a | |
refactor is <em>not</em>:</p><pre><code class="language-js">const sum = (...args) =&gt; args.reduce((s, n) =&gt; s + n, 0) | |
</code></pre><p>This is awesome, our <code>sum</code> is more capable, but what we did was <em>not</em> | |
technically a refactor, it was an enhancement. Let&#x27;s compare:</p><pre><code>| call | result before | result after | | |
|--------------|---------------|--------------| | |
| sum() | NaN | 0 | | |
| sum(1) | NaN | 1 | | |
| sum(1, 2) | 3 | 3 | | |
| sum(1, 2, 3) | 3 | 6 | | |
</code></pre><p>So why was this not a refactor? It&#x27;s because we are &quot;changing its external | |
behavior.&quot; Now, this change is desirable, but it is a change.</p><p>So what does all this have to do with <code>useEffect</code>? Let&#x27;s look at another example | |
of our counter component as a class with a new feature:</p><pre><code class="language-jsx">class Counter extends React.Component { | |
state = { | |
count: Number(window.localStorage.getItem(&#x27;count&#x27;) || 0), | |
} | |
increment = () =&gt; this.setState(({count}) =&gt; ({count: count + 1})) | |
componentDidMount() { | |
window.localStorage.setItem(&#x27;count&#x27;, this.state.count) | |
} | |
componentDidUpdate(prevProps, prevState) { | |
if (prevState.count !== this.state.count) { | |
window.localStorage.setItem(&#x27;count&#x27;, this.state.count) | |
} | |
} | |
render() { | |
return &lt;button onClick={this.increment}&gt;{this.state.count}&lt;/button&gt; | |
} | |
} | |
</code></pre><p>Ok, so we&#x27;re saving the value of <code>count</code> in <code>localStorage</code> using | |
<code>componentDidMount</code> and <code>componentDidUpdate</code>. Here&#x27;s what our | |
implementation-details-free test would look like:</p><pre><code class="language-jsx">// __tests__/counter.js | |
import React from &#x27;react&#x27; | |
import &#x27;react-testing-library/cleanup-after-each&#x27; | |
import {render, fireEvent, cleanup} from &#x27;react-testing-library&#x27; | |
import Counter from &#x27;../counter.js&#x27; | |
afterEach(() =&gt; { | |
window.localStorage.removeItem(&#x27;count&#x27;) | |
}) | |
test(&#x27;counter increments the count&#x27;, () =&gt; { | |
const {container} = render(&lt;Counter /&gt;) | |
const button = container.firstChild | |
expect(button.textContent).toBe(&#x27;0&#x27;) | |
fireEvent.click(button) | |
expect(button.textContent).toBe(&#x27;1&#x27;) | |
}) | |
test(&#x27;reads and updates localStorage&#x27;, () =&gt; { | |
window.localStorage.setItem(&#x27;count&#x27;, 3) | |
const {container, rerender} = render(&lt;Counter /&gt;) | |
const button = container.firstChild | |
expect(button.textContent).toBe(&#x27;3&#x27;) | |
fireEvent.click(button) | |
expect(button.textContent).toBe(&#x27;4&#x27;) | |
expect(window.localStorage.getItem(&#x27;count&#x27;)).toBe(&#x27;4&#x27;) | |
}) | |
</code></pre><p>That test passes! Woo! Now let&#x27;s &quot;refactor&quot; this to hooks again with these new | |
features:</p><pre><code class="language-jsx">import React, {useState, useEffect} from &#x27;react&#x27; | |
function Counter() { | |
const [count, setCount] = useState(() =&gt; | |
Number(window.localStorage.getItem(&#x27;count&#x27;) || 0), | |
) | |
const incrementCount = () =&gt; setCount(c =&gt; c + 1) | |
useEffect(() =&gt; { | |
window.localStorage.setItem(&#x27;count&#x27;, count) | |
}, [count]) | |
return &lt;button onClick={incrementCount}&gt;{count}&lt;/button&gt; | |
} | |
export default Counter | |
</code></pre><p>Cool, as far as the user is concerned, this component will work <em>exactly</em> the | |
same as it had before. But it&#x27;s actually working differently from how it was | |
before. The real trick here is that <strong>the</strong> <code>**useEffect**</code> <strong>callback is | |
<em>scheduled</em> to run at a later time</strong>. So before, we set the value of | |
<code>localStorage</code> synchronously after rendering. Now, it&#x27;s scheduled to run later | |
after rendering. Why is this? Let&#x27;s checkout | |
<a href="https://reactjs.org/docs/hooks-effect.html#detailed-explanation">this tip from the React Hooks docs</a>:</p><blockquote><p>Unlike <code>componentDidMount</code> or <code>componentDidUpdate</code>, effects scheduled with | |
<code>useEffect</code> don&#x27;t block the browser from updating the screen. This makes your | |
app feel more responsive. The majority of effects don&#x27;t need to happen | |
synchronously. In the uncommon cases where they do (such as measuring the | |
layout), there is a separate | |
<a href="https://reactjs.org/docs/hooks-reference.html#uselayouteffect"><code>useLayoutEffect</code></a> | |
Hook with an API identical to <code>useEffect</code>.</p></blockquote><p>Ok, so by using <code>useEffect</code> that&#x27;s better for performance! Awesome! We&#x27;ve made | |
an enhancement to our component and our component code is actually simpler to | |
boot! NEAT!</p><p>However, this is <em>not</em> a refactor. It&#x27;s actually a change in behavior. As far as | |
the <em>end</em> user is concerned, that change is unobservable. In our efforts to | |
ensure that our tests are free of implementation details, that change should be | |
unobservable as well.</p><p>Whelp, thanks to the new <a href="https://reactjs.org/docs/test-utils.html#act"><code>act</code></a> | |
utility from <a href="https://reactjs.org/docs/test-utils.html"><code>react-dom/test-utils</code></a> | |
we can make that happen. So <code>react-testing-library</code> integrates with that utility | |
and makes it so all our tests continue to pass as written, allowing the tests we | |
write to be free of implementation details and continue to resemble the way our | |
software is used as closely as possible.</p><h3>What about render props components?</h3><p>This is probably my favorite actually. Here&#x27;s a simple counter render prop | |
component:</p><pre><code class="language-js">class Counter extends React.Component { | |
state = {count: 0} | |
increment = () =&gt; this.setState(({count}) =&gt; ({count: count + 1})) | |
render() { | |
return this.props.children({ | |
count: this.state.count, | |
increment: this.increment, | |
}) | |
} | |
} | |
// usage: | |
// &lt;Counter&gt; | |
// {({ count, increment }) =&gt; &lt;button onClick={increment}&gt;{count}&lt;/button&gt;} | |
// &lt;/Counter&gt; | |
</code></pre><p>Here&#x27;s how I would test this:</p><pre><code class="language-jsx">// __tests__/counter.js | |
import React from &#x27;react&#x27; | |
import &#x27;react-testing-library/cleanup-after-each&#x27; | |
import {render, fireEvent} from &#x27;react-testing-library&#x27; | |
import Counter from &#x27;../counter.js&#x27; | |
function renderCounter(props) { | |
let utils | |
const children = jest.fn(stateAndHelpers =&gt; { | |
utils = stateAndHelpers | |
return null | |
}) | |
return { | |
...render(&lt;Counter {...props}&gt;{children}&lt;/Counter&gt;), | |
children, | |
// this will give us access to increment and count | |
...utils, | |
} | |
} | |
test(&#x27;counter increments the count&#x27;, () =&gt; { | |
const {children, increment} = renderCounter() | |
expect(children).toHaveBeenCalledWith(expect.objectContaining({count: 0})) | |
increment() | |
expect(children).toHaveBeenCalledWith(expect.objectContaining({count: 1})) | |
}) | |
</code></pre><p>Ok, so let&#x27;s refactor the counter to a component that uses hooks:</p><pre><code class="language-js">function Counter(props) { | |
const [count, setCount] = useState(0) | |
const increment = () =&gt; setCount(currentCount =&gt; currentCount + 1) | |
return props.children({ | |
count: count, | |
increment, | |
}) | |
} | |
</code></pre><p>Cool, and because we wrote our test the way we did, it&#x27;s actually still passing. | |
Woo! BUT! As we learned from | |
&quot;<a href="https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-render-props">React Hooks: What&#x27;s going to happen to render props?</a>&quot; | |
custom hooks are a better primitive for code sharing in React. So let&#x27;s rewrite | |
this to a custom hook:</p><pre><code class="language-js">function useCounter() { | |
const [count, setCount] = useState(0) | |
const increment = () =&gt; setCount(currentCount =&gt; currentCount + 1) | |
return {count, increment} | |
} | |
export default useCounter | |
// usage: | |
// function Counter() { | |
// const {count, increment} = useCounter() | |
// return &lt;button onClick={increment}&gt;{count}&lt;/button&gt; | |
// } | |
</code></pre><p>Awesome... but how do we test <code>useCounter</code>? And wait! We can&#x27;t update our entire | |
codebase to the new <code>useCounter</code>! We were using the <code>&lt;Counter /&gt;</code> render prop | |
based component in like three hundred places!? Rewrites are the worst!</p><p>Nah, I got you. Do this instead:</p><pre><code class="language-js">function useCounter() { | |
const [count, setCount] = useState(0) | |
const increment = () =&gt; setCount(currentCount =&gt; currentCount + 1) | |
return {count, increment} | |
} | |
const Counter = ({children, ...props}) =&gt; children(useCounter(props)) | |
export default Counter | |
export {useCounter} | |
</code></pre><p>Our new <code>&lt;Counter /&gt;</code> render-prop based component there is actually <em>exactly</em> | |
the same as the one we had before. So this is a true refactor. But now anyone | |
who can take the time to upgrade can use our <code>useCounter</code>custom hook.</p><p>Oh, and guess what. Our tests are still passing!!! WHAT! How neat right?</p><p>So when everyone&#x27;s upgraded we can remove the Counter function component right? | |
You may be able to do that, but I would actually move it to the <code>__tests__</code> | |
because <em>that&#x27;s</em> how I like testing custom hooks! I prefer making a render-prop | |
based component out of a custom hook, and actually rendering that and asserting | |
on what the function is called with.</p><p>Fun trick right? I show you how to do this in | |
<a href="https://kcd.im/refactor-react">my new course on egghead.io</a>. Enjoy!</p><h2>What about hooks libraries?</h2><p>If you&#x27;re writing a generic or open source hook, then you may want to test it | |
without a specific component in mind. In that case, I recommend using | |
<a href="https://www.npmjs.com/package/react-hooks-testing-library"><code>react-hooks-testing-library</code></a>.</p><h2>Conclusion</h2><p>One of the best things you can do before you refactor code is have a good test | |
suite/type definitions in place so when you inadvertently break something you | |
can be made aware of the mistake right away. But <strong>your test suite can&#x27;t do you | |
any good if you have to throw it away when you refactor it.</strong> Take my advice: | |
<a href="https://kcd.im/imp-deets">avoid implementation details</a> in your tests. Write | |
tests that will work today with classes, and in the future if those classes are | |
refactored to functions with hooks. Good luck!</p><p><strong>Learn more about React Hooks from me</strong>:</p><p>If you thought this was interesting, I highly recommend you watch these (while | |
they&#x27;re both still free):</p><ul><li><a href="http://kcd.im/hooks-and-suspense">React Hooks and Suspense</a> — A great | |
primer on Hooks and Suspense</li><li><a href="http://kcd.im/refactor-react">Simplify React Apps with React Hooks</a> — Let&#x27;s | |
take some real-world class components and refactor them to function components | |
with hooks.</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://github.com/rescripts/rescripts">rescripts</a> — 💥 Use the latest | |
react-scripts with custom configurations for Babel, ESLint, TSLint, | |
Webpack,... ∞ by <a href="https://twitter.com/hsolvz">Harry Solovay</a></li><li><a href="https://youtu.be/k6KcaMffxac?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Contributing to Open Source on GitHub for beginners</a> | |
— A talk I gave at my Alma mater (BYU) this last week</li><li><a href="https://youtu.be/HL6paXyx6hM?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">Make a SUPER simple personal URL shortener with Netlify</a> | |
(I&#x27;m still livestreaming almost every week day at | |
<a href="https://kcd.im/devtips">kcd.im/devtips</a>)</li><li><a href="https://twitter.com/jamiebuilds/status/1022568918949408768">The three browsers holding JavaScript back the most are:</a>... | |
An interesting thread by <a href="https://twitter.com/jamiebuilds">Jamie Kyle</a>.</li><li><a href="https://medium.com/emotion-js/announcing-emotion-10-f1a4b17b8ccd">Emotion 10 released!</a> | |
— This is still my favorite CSS-in-JS solution and | |
<a href="https://twitter.com/tkh44/status/1070901663622283265">this is why</a> I prefer | |
it over styled-components.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-my-tests">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React Hooks: What's going to happen to react context?]]></title> | |
<description><![CDATA[Earlier this year, the React team introduced the first official context API. | |
I blogged about that new API and people got | |
sufficiently and reasonably hyped. One common complaint that I knew people were going to have when applying it | |
practically was…]]></description> | |
<link>https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-react-context</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-react-context</guid> | |
<pubDate>Mon, 17 Dec 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Earlier this year, the React team introduced the first official context API. | |
<a href="https://kentcdodds.com/blog/reacts-new-context-api">I blogged about that new API</a> and people got | |
sufficiently and reasonably hyped.</p><p>One common complaint that I knew people were going to have when applying it | |
practically was the fact that the context consumer is a render-prop based API. | |
This can lead to a lot of nesting when you need to consume multiple contexts and | |
other render-prop based APIs as well (for logic reuse). So I addressed that in | |
the blog post by suggesting that you could combine all of the render-prop based | |
APIs into a single function component and consume that:</p><pre><code class="language-jsx">const ThemeContext = React.createContext(&#x27;light&#x27;) | |
class ThemeProvider extends React.Component { | |
/* code */ | |
} | |
const ThemeConsumer = ThemeContext.Consumer | |
const LanguageContext = React.createContext(&#x27;en&#x27;) | |
class LanguageProvider extends React.Component { | |
/* code */ | |
} | |
const LanguageConsumer = LanguageContext.Consumer | |
function AppProviders({children}) { | |
return ( | |
&lt;LanguageProvider&gt; | |
&lt;ThemeProvider&gt;{children}&lt;/ThemeProvider&gt; | |
&lt;/LanguageProvider&gt; | |
) | |
} | |
function ThemeAndLanguageConsumer({children}) { | |
return ( | |
&lt;LanguageConsumer&gt; | |
{language =&gt; ( | |
&lt;ThemeConsumer&gt;{theme =&gt; children({language, theme})}&lt;/ThemeConsumer&gt; | |
)} | |
&lt;/LanguageConsumer&gt; | |
) | |
} | |
function App() { | |
return ( | |
&lt;AppProviders&gt; | |
&lt;ThemeAndLanguageConsumer&gt; | |
{({theme, language}) =&gt; ( | |
&lt;div&gt; | |
{theme} and {language} | |
&lt;/div&gt; | |
)} | |
&lt;/ThemeAndLanguageConsumer&gt; | |
&lt;/AppProviders&gt; | |
) | |
} | |
</code></pre><p>As much as this solution works thanks to the composability of React components, | |
I&#x27;m still not super thrilled with it. And I&#x27;m not the only one:</p><blockquote><p><em>We&#x27;ve heard feedback that adopting the new render prop API can be difficult | |
in class components. So we&#x27;ve added a convenience API to</em> &gt; | |
<a href="https://reactjs.org/docs/context.html#classcontexttype"><em>consume a context value from within a class component</em></a><em>. — </em><a href="https://reactjs.org/blog/2018/10/23/react-v-16-6.html"><em>React v16.6.0: lazy, memo and contextType</em></a></p></blockquote><p>This new convenience API means that if you use a class component and you&#x27;re only | |
consuming one context, you can simply define a static property called | |
<code>contextType</code> and assign it to the context you want to consume, then you can | |
access the context via <code>this.context</code>. It&#x27;s pretty neat and a nice trick for | |
common cases where you only consume a single context.</p><p>I&#x27;ve used this convenience API and I love it. But I&#x27;m even more excited about | |
the implications that React Hooks have for the future of React context. Let&#x27;s | |
rewrite what we have above with the upcoming (ALPHA!) <code>useContext</code> hook:</p><pre><code class="language-jsx">const ThemeContext = React.createContext(&#x27;light&#x27;) | |
class ThemeProvider extends React.Component { | |
/* code */ | |
} | |
const LanguageContext = React.createContext(&#x27;en&#x27;) | |
class LanguageProvider extends React.Component { | |
/* code */ | |
} | |
function AppProviders({children}) { | |
return ( | |
&lt;LanguageProvider&gt; | |
&lt;ThemeProvider&gt;{children}&lt;/ThemeProvider&gt; | |
&lt;/LanguageProvider&gt; | |
) | |
} | |
function App() { | |
const theme = useContext(ThemeContext) | |
const language = useContext(LanguageContext) | |
return ( | |
&lt;div&gt; | |
{theme} and {language} | |
&lt;/div&gt; | |
) | |
} | |
ReactDOM.render( | |
&lt;AppProviders&gt; | |
&lt;App /&gt; | |
&lt;/AppProviders&gt;, | |
document.getElementById(&#x27;root&#x27;), | |
) | |
</code></pre><p>WOWZA! As powerful as the render-prop based consumers are, this is even easier | |
to read, understand, refactor, and maintain! And it&#x27;s not just less code for | |
less code&#x27;s sake. Besides, often when we reduce the amount of code we also | |
reduce the clarity of communication that code can give to us. But in this case, | |
it&#x27;s less code <em>and</em> it&#x27;s easier to understand. I think that&#x27;s a big win and a | |
huge feature of the new hooks API.</p><p>Another big feature of React hooks is the fact that it&#x27;s completely opt-in and | |
backward compatible. I&#x27;m given such a huge amount of comfort knowing that | |
Facebook can&#x27;t make decisions that will cause grief to the engineers who are | |
working on <em>the</em> oldest and one of the largest React codebases in the world. The | |
fact that React has incrementally taken us to this new world of hooks is just | |
fantastic. Thanks React team! Looking forward to the official release!</p><h3>Conclusion</h3><p>One of the coolest things about React is that it allows us to focus on solving | |
real-world problems without normally having to get too close to the | |
implementation of things. It&#x27;s been a long time since I had to deal with | |
cross-browser or performance issues with any degree of regularity. And now React | |
is taking it even further and simplifying things so the code that I do write is | |
simpler to read, understand refactor, and maintain. I just love that. Makes me | |
wonder if there may be some things I could do about my code to simplify things | |
for other people as well 🤔.</p><p>Until next time! Good luck! 👋</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="http://kcd.im/refactor-react">Simplify React Apps with React Hooks and Suspense</a> — My | |
new egghead course... of course!</li><li><a href="http://kcd.im/shurlan">Shurlan</a> — I WON <a href="https://nanowrimo.org">NANOWRIMO</a> | |
THIS YEAR! That means that I successfully wrote 50,000 words of a novel in the | |
month of November (for perspective, Harry Potter book 1 is 76k words). It was | |
a wild month, and it was tons of fun. And you can read what I ended up with. | |
It&#x27;s a fantasy novel about a utopian world where things start to go bad and a | |
14-year-old girl is tasked with stopping a rebellion from inadvertently | |
destroying the city. I think you&#x27;ll love the characters, plot, and magic | |
system :)</li><li><a href="https://reactjs.org/blog/2018/11/27/react-16-roadmap.html">React 16.x Roadmap</a> — Tl;DR: | |
React 16.6: Suspense for Code Splitting (already shipped), React 16.7: React | |
Hooks (~Q1 2019), React 16.8: Concurrent Mode (~Q2 2019), React 16.9: Suspense | |
for Data Fetching (~mid 2019)</li><li><a href="https://youtu.be/xcZXS_VEJS0?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Modern React Workshop: Hooks &amp; Suspense</a> — a | |
recording of a livestream I did last week at PayPal. | |
<a href="https://github.com/kentcdodds/modern-react">Here&#x27;s the workshop repo</a> and | |
<a href="https://youtu.be/NKAfuguroRY?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">here&#x27;s the part 2</a>.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-react-context">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React Hooks: What's going to happen to render props?]]></title> | |
<description><![CDATA[Current Available Translations: Korean About a year ago, I published | |
"How to give rendering control to users with prop getters" . | |
In that post, I show the entire implementation (at the time) of | |
react-toggled which I actually | |
built for the sole…]]></description> | |
<link>https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-render-props</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-render-props</guid> | |
<pubDate>Mon, 10 Dec 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Current Available Translations:</p><ul><li><a href="https://edykim.com/ko/post/react-hooks-whats-going-to-happen-to-render-props">Korean</a></li></ul><p>About a year ago, I published | |
<a href="https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters">&quot;How to give rendering control to users with prop getters&quot;</a>. | |
In that post, I show the entire implementation (at the time) of | |
<a href="https://github.com/kentcdodds/react-toggled"><code>react-toggled</code></a> which I actually | |
built for the sole purpose of teaching some of the patterns that I used in | |
<a href="https://github.com/downshift-js/downshift"><code>downshift</code></a>. It&#x27;s a much smaller | |
and simpler component that implements many of the same patterns as downshift so | |
it served as a great way to teach the prop getters pattern.</p><p>Both react-toggled and downshift use the render prop pattern as a mechanism for | |
React component logic code sharing. As I explained in my blog post | |
<a href="https://kentcdodds.com/blog/when-to-not-use-render-props">&quot;When to NOT use Render Props&quot;</a>, that&#x27;s the | |
primary use case for the render prop pattern. But that&#x27;s also the primary use | |
case for React Hooks as well. And React Hooks are WAY simpler than class | |
components + render props.</p><p>So does that mean that when React Hooks are stable you wont need render props at | |
all anymore? <strong>No!</strong> I can think of two scenarios where the render prop pattern | |
will still be very useful, and I&#x27;ll share those with you in a moment. But let&#x27;s | |
go ahead and establish my claim that hooks are simpler by comparing the current | |
version of <code>react-toggled</code> with a hooks-based implementation.</p><p>If you&#x27;re interested, | |
<a href="https://github.com/kentcdodds/react-toggled/blob/8452a1f2a4ec7b64588cd8c9812e0faf8deb0271/src/index.js">here&#x27;s the current source for</a> | |
<a href="https://github.com/kentcdodds/react-toggled/blob/8452a1f2a4ec7b64588cd8c9812e0faf8deb0271/src/index.js"><code>react-toggled</code></a>.</p><p>Here&#x27;s a typical usage of <code>react-toggled</code>:</p><pre><code class="language-jsx">function App() { | |
return ( | |
&lt;Toggle&gt; | |
{({on, toggle}) =&gt; &lt;button onClick={toggle}&gt;{on ? &#x27;on&#x27; : &#x27;off&#x27;}&lt;/button&gt;} | |
&lt;/Toggle&gt; | |
) | |
} | |
</code></pre><p>If all we wanted was simple toggle functionality, our hook version would be:</p><pre><code class="language-js">function useToggle(initialOn = false) { | |
const [on, setOn] = useState(initialOn) | |
const toggle = () =&gt; setOn(!on) | |
return {on, toggle} | |
} | |
</code></pre><p>Then people could use that like so:</p><pre><code class="language-jsx">function App() { | |
const {on, toggle} = useToggle() | |
return &lt;button onClick={toggle}&gt;{on ? &#x27;on&#x27; : &#x27;off&#x27;}&lt;/button&gt; | |
} | |
</code></pre><p>Cool! A lot simpler! But the Toggle component in react-toggled actually supports | |
a lot more. For one thing, it provides a helper called <code>getTogglerProps</code> which | |
will give you the correct props you need for a toggler to work (including | |
<code>aria</code>attributes for accessibility). So let&#x27;s make that work:</p><pre><code class="language-js">// returns a function which calls all the given functions | |
const callAll = (...fns) =&gt; (...args) =&gt; fns.forEach(fn =&gt; fn &amp;&amp; fn(...args)) | |
function useToggle(initialOn = false) { | |
const [on, setOn] = useState(initialOn) | |
const toggle = () =&gt; setOn(!on) | |
const getTogglerProps = (props = {}) =&gt; ({ | |
&#x27;aria-expanded&#x27;: on, | |
tabIndex: 0, | |
...props, | |
onClick: callAll(props.onClick, toggle), | |
}) | |
return { | |
on, | |
toggle, | |
getTogglerProps, | |
} | |
} | |
</code></pre><p>And now our <code>useToggle</code> hook can use the <code>getTogglerProps</code>:</p><pre><code class="language-jsx">function App() { | |
const {on, getTogglerProps} = useToggle() | |
return &lt;button {...getTogglerProps()}&gt;{on ? &#x27;on&#x27; : &#x27;off&#x27;}&lt;/button&gt; | |
} | |
</code></pre><p>And it&#x27;s more accessible and stuff. Neat right? Well, what if I don&#x27;t need the | |
<code>getTogglerProps</code> for my use case? Let&#x27;s split this up a bit:</p><pre><code class="language-js">// returns a function which calls all the given functions | |
const callAll = (...fns) =&gt; (...args) =&gt; fns.forEach(fn =&gt; fn &amp;&amp; fn(...args)) | |
function useToggle(initialOn = false) { | |
const [on, setOn] = useState(initialOn) | |
const toggle = () =&gt; setOn(!on) | |
return {on, toggle} | |
} | |
function useToggleWithPropGetter(initialOn) { | |
const {on, toggle} = useToggle(initialOn) | |
const getTogglerProps = (props = {}) =&gt; ({ | |
&#x27;aria-expanded&#x27;: on, | |
tabIndex: 0, | |
...props, | |
onClick: callAll(props.onClick, toggle), | |
}) | |
return {on, toggle, getTogglerProps} | |
} | |
</code></pre><p>And we could do the same thing to support the <code>getInputTogglerProps</code> and | |
<code>getElementTogglerProps</code> helpers that <code>react-toggled</code> currently supports. This | |
would actually allow us to easily tree-shake out those extra utilities that our | |
app is not using, something that would be pretty unergonomic to do with a render | |
props solution (not impossible, just kinda ugly).</p><p><strong>But Kent!</strong> I don&#x27;t want to go and refactor all the places in my app that use | |
the render prop API to use the new hooks API!!</p><p>Never fear! Check this out:</p><pre><code class="language-js">const Toggle = ({children, ...props}) =&gt; children(useToggle(props)) | |
</code></pre><p>There&#x27;s your render prop component. You can use that just like you were using | |
the old one and migrate over time. In fact, this is how I recommend testing | |
custom hooks!</p><p>There&#x27;s a little more to this (like how do we port the control props pattern to | |
react hooks for example). I&#x27;m going to leave that to you to discover. Once | |
you&#x27;ve tried it out for a little bit, then | |
<a href="https://youtu.be/_eVyLVFlSQk?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">watch me do it</a>. | |
There&#x27;s a catch with the way we&#x27;ve been testing things a bit that change | |
slightly with hooks (thanks to JavaScript closures).</p><h3>The remaining use case for render props</h3><p>Ok, so we can refactor our components to use hooks, and even continue to export | |
react components with a render prop-based API (you might be interested, you may | |
even consider going all out with | |
<a href="https://americanexpress.io/hydra">the hydra pattern</a>). But let&#x27;s imagine we&#x27;re | |
now in a future where we don&#x27;t need render props for logic reuse and everyone&#x27;s | |
using hooks. Is there any reason to continue writing or using components that | |
expose a render props API?</p><p>YES! Observe! Here&#x27;s | |
<a href="https://github.com/downshift-js/downshift/blob/9b3467dce2be59832765277570857de5679d8392/stories/examples/windowing-with-react-virtualized.js">an example of using downshift with react-virtualized</a>. | |
Here&#x27;s the relevant bit:</p><pre><code class="language-jsx">&lt;List | |
// ... some props | |
rowRenderer={({key, index, style}) =&gt; ( | |
&lt;div | |
// ... some props | |
/&gt; | |
)} | |
/&gt; | |
</code></pre><p>Checkout that <code>rowRenderer</code> prop there. Do you know what that is? IT&#x27;S A RENDER | |
PROP! What!? 🙀 That&#x27;s inversion of control in all its glory with render props | |
right there. That&#x27;s a prop that <code>react-virtualized</code> uses to delegate control of | |
rendering rows in a list to you the user of the component. If | |
<code>react-virtualized</code> were to be rewritten to use hooks, <em>maybe</em> it could accept | |
the <code>rowRenderer</code> as an argument to the <code>useVirtualized</code> hook, but I don&#x27;t | |
really see any benefit to that over it&#x27;s current API. So I think render props | |
(and this style of inversion of control) are here to stay for use cases like | |
this.</p><h3>Conclusion</h3><p>I hope you find this interesting and helpful. Remember that React hooks are | |
still in alpha and subject to change. They are also completely opt-in and will | |
not require any breaking changes to React&#x27;s API. I think that&#x27;s a great thing. | |
Don&#x27;t go rewriting your apps! Refactor them (once hooks are stable)!</p><p>Good luck!</p><p><strong>Learn more about Refactoring to React Hooks from me</strong>:</p><p><a href="https://kentcdodds.com/blog/introducing-a-new-course-simplify-react-apps-with-react-hooks-and-suspense">My new egghead.io course</a> | |
will show you how to refactor a typical app&#x27;s components to use react hooks (and | |
React.lazy/suspense). It&#x27;s a good time!</p><p>Also, check out | |
<a href="http://kcd.im/hooks-and-suspense">this free egghead playlist about hooks and suspense</a>!</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://github.com/dominictarr/event-stream/issues/116">npm malware event</a>: | |
The package <code>event-stream</code> was published with a dependency that tried to steal | |
bitcoin wallets. <code>event-stream</code> is downloaded ~2million times per week, so | |
it&#x27;s likely you&#x27;ve been infected. Check | |
<a href="https://blog.npmjs.org">the npm blog</a>, I&#x27;m sure they&#x27;ll post more about it | |
soon.</li><li><a href="https://reactpodcast.simplecast.fm/29">React Podcast: 29: Don&#x27;t Rewrite Your App for Hooks and Suspense with Jared Palmer</a></li><li><a href="https://github.com/developit/htm"><code>htm</code></a> by | |
<a href="https://twitter.com/_developit/status/1065026506068504577">Jason Miller</a> | |
looks pretty slick. I still prefer JSX, but I can appreciate what he&#x27;s doing | |
there and the fact that there&#x27;s no extra special syntax for things JavaScript | |
can do (like map an array) is a major plus :)</li><li><a href="https://medium.com/styled-components/announcing-native-support-for-the-css-prop-in-styled-components-245ca5252feb">Announcing native support for the css prop in styled-components 🎉</a> — This | |
was always one of my biggest grievances with styled-components and a big | |
reason I preferred <a href="https://emotion.sh">emotion</a>. I still prefer emotion, but | |
I&#x27;m really excited that styled-components has this feature now! Stop naming | |
things &quot;Container&quot; and &quot;Wrapper!&quot;</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-render-props">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Introducing a new course: Simplify React Apps with React Hooks and Suspense]]></title> | |
<description><![CDATA[Learn about the massive improvements coming to function components in React via | |
a fresh new course showing you how to refactor an existing app to these new and | |
upcoming APIs. Ok, before I get into things, can I just say that this course artwork by…]]></description> | |
<link>https://kentcdodds.com/blog/introducing-a-new-course-simplify-react-apps-with-react-hooks-and-suspense</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/introducing-a-new-course-simplify-react-apps-with-react-hooks-and-suspense</guid> | |
<pubDate>Mon, 03 Dec 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p><em>Learn about the massive improvements coming to function components in React via | |
a fresh new course showing you how to refactor an existing app to these new and | |
upcoming APIs.</em></p><p><em>Ok, before I get into things, can I just say that this course artwork by</em> | |
<a href="https://twitter.com/mappletons"><em>Maggie Appleton</em></a> <em>and</em> | |
<a href="https://twitter.com/MaximalGFX"><em>Maxime Bourgeois</em></a> <em>is just the absolute best. | |
It&#x27;s just so good. 😍</em></p><p>I&#x27;m super excited to share this course with you. I&#x27;ve been using React full time | |
for almost three years now and I&#x27;ve never been more | |
<a href="https://youtu.be/0jlTw2XI7I8?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">excited (!!)</a> | |
about writing components than when I started playing around with Hooks and | |
Suspense. Let&#x27;s get a quick rundown of what you can expect from the course:</p><h3><a href="https://egghead.io/courses/simplify-react-apps-with-react-hooks-and-suspense">About the course</a></h3><p>With the massive improvements to function components in React via hooks and | |
suspense, you may be interested in seeing how to refactor a typical class | |
component to a simpler class component that uses React Suspense and Hooks | |
features. In this course, Kent will take | |
<a href="https://github.com/kentcdodds/react-github-profile">a modern React codebase</a> | |
that uses classes and refactor the entire thing to use function components as | |
much as possible. We&#x27;ll look at state, side effects, async code, caching, and | |
more!</p><p>Want a primer on hooks and suspense? | |
<a href="https://egghead.io/playlists/react-hooks-and-suspense-650307f2">Watch my React Hooks and Suspense Playlist</a>!</p><p><em>note: React Hooks is</em> <strong><em>alpha</em></strong> <em>and subject to change. The React team has</em> | |
<a href="https://reactjs.org/blog/2018/11/27/react-16-roadmap.html"><em>the 16.x roadmap here</em></a><em>.</em></p><h3><a href="https://egghead.io/lessons/react-introduction-to-refactoring-a-react-application-to-react-hooks-and-react-suspense">Introduction to Refactoring a React Application to React Hooks and React Suspense</a></h3><p>Let&#x27;s get a quick overview of what this course is all about and how it&#x27;s been | |
structured to make sure you&#x27;re as productive as possible with these new | |
features.</p><h3><a href="https://egghead.io/lessons/react-refactor-a-class-component-with-react-hooks-to-a-function">Refactor a Class Component with React hooks to a Function</a></h3><p>We have a render prop based class component that allows us to make a GraphQL | |
request with a given query string and variables and uses a GitHub graphql client | |
that is in React context to make the request. Let&#x27;s refactor this to a function | |
component that uses the hooks useReducer, useContext, and useEffect.</p><h3><a href="https://egghead.io/lessons/react-handle-deep-object-comparison-in-react-s-useeffect-hook-with-the-useref-hook">Handle Deep Object Comparison in React&#x27;s useEffect hook with the useRef Hook</a></h3><p>The second argument to React&#x27;s <code>useEffect</code> hook is an array of dependencies for | |
your <code>useEffect</code> callback. When any value in that array changes, the effect | |
callback is re-run. But the <code>variables</code> object we&#x27;re passing to that array is | |
created during render, so our effect will be re-run every render even if the | |
shape of the object is the same. So let&#x27;s solve this by doing our own equality | |
check from within the effect callback.</p><h3><a href="https://egghead.io/lessons/react-safely-setstate-on-a-mounted-react-component-through-the-useeffect-hook">Safely setState on a Mounted React Component through the useEffect Hook</a></h3><p>In the class version of this component, we had a method called <code>safeSetState</code> | |
which would check whether the component was still mounted before trying to call | |
<code>setState</code>. This is because our graphql client library is unable to cancel | |
in-flight requests. Let&#x27;s make that same kind of thing work by tracking the | |
mounted state of our component using the <code>useRef</code> and <code>useEffect</code> hooks.</p><h3><a href="https://egghead.io/lessons/react-extract-generic-react-hook-code-into-custom-react-hooks">Extract Generic React Hook Code into Custom React Hooks</a></h3><p>Because hooks code is regular JavaScript, extracting it to its own function is | |
trivial and enables code sharing in a really nice way. It also allows us to | |
encapsulate and separate concerns really cleanly. Custom hooks also compose | |
really nicely together to build more complex hooks out of more primitive ones. | |
Let&#x27;s do this by creating a <code>useSetState</code> and <code>useSafeSetState</code>custom hook.</p><p>If you would like a more comprehensive <code>useSetState</code> hook, give | |
<a href="https://github.com/suchipi/use-legacy-state"><code>use-legacy-state</code></a> a try.</p><h3><a href="https://egghead.io/lessons/react-track-values-over-the-course-of-renders-with-react-useref-in-a-custom-useprevious-hook">Track Values Over the Course of Renders with React useRef in a Custom usePrevious Hook</a></h3><p>Our hook to track the previous values looks pretty useful, so let&#x27;s extract that | |
into it&#x27;s own custom React Hook called <code>usePrevious</code>.</p><h3><a href="https://egghead.io/lessons/react-deeply-compare-inputs-in-a-custom-react-hook-for-useeffect">Deeply Compare Inputs in a Custom React Hook for useEffect</a></h3><p>It would be nice if <code>useEffect</code> did the deep value comparison for us. Why don&#x27;t | |
we make our own custom hook that does that for us? In this lesson we&#x27;ll create a | |
<code>useDeepCompareEffect</code> which will allow us to use it just like a <code>useEffect</code>and | |
allow us to just pass the inputs.</p><h3><a href="https://egghead.io/lessons/react-refactor-a-react-class-component-with-usecontext-and-usestate-hooks">Refactor a React Class Component with useContext and useState Hooks</a></h3><p>We&#x27;ve got a pretty simple User class component that manages a bit of state and | |
uses some context. Let&#x27;s refactor this over to a function component that uses | |
the <code>useContext</code> and <code>useState</code> hooks.</p><h3><a href="https://egghead.io/lessons/react-refactor-a-render-prop-component-to-a-custom-react-hook">Refactor a render Prop Component to a Custom React Hook</a></h3><p>Our <code>&lt;Query /&gt;</code> component is a render prop based component that the <code>&lt;User /&gt;</code> | |
component uses. But because it doesn&#x27;t render anything, we can actually just | |
change it to a custom hook. Let&#x27;s create a <code>useQuery</code> hook that returns the | |
state from the hooks the Query component uses and use that instead. But we&#x27;ll | |
preserve the component so we don&#x27;t have to refactor everywhere that uses the | |
Query render prop based component as well and we can keep our tests passing as | |
they are.</p><h3><a href="https://egghead.io/lessons/react-handle-componentdidmount-and-componentwillunmount-in-react-component-refactor-to-hooks">Handle componentDidMount and componentWillUnmount in React Component Refactor to Hooks</a></h3><p>Let&#x27;s refactor our <code>GitHubClientProvider</code> class component to a function | |
component that uses hooks. This one&#x27;s pretty interesting because we can use | |
<code>useEffect</code> to encapsulate everything we need for a single effect, truly | |
separating that concern within our component.</p><h3><a href="https://egghead.io/lessons/react-dynamically-import-react-components-with-react-lazy-and-suspense">Dynamically Import React Components with React.lazy and Suspense</a></h3><p>With React 16.6.0, React Suspense was officially released as a stable feature | |
(with limited support for <code>React.lazy</code>). Let&#x27;s refactor our lazily-loaded | |
components that are using | |
<a href="https://github.com/jamiebuilds/react-loadable"><code>react-loadable</code></a> to components | |
that use the built-in <code>React.lazy</code>feature.</p><h3><a href="https://egghead.io/lessons/react-preload-react-components-with-the-useeffect-hook">Preload React Components with the useEffect Hook</a></h3><p>While users are filling out the form on our home page, it would be a good idea | |
to pre-load the next page they will be going to so they don&#x27;t have to wait for | |
it to load once they&#x27;ve finished filling out the form. React&#x27;s <code>useEffect</code> hook | |
makes this really easy.</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/introducing-a-new-course-simplify-react-apps-with-react-hooks-and-suspense">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How Gratitude can make you a better developer]]></title> | |
<description><![CDATA[This week in the United States we celebrate | |
Thanksgiving Day . In the US, the | |
celebration was started by Pilgrims who came to America in the 1620s on | |
the Mayflower . They had a pretty | |
rough time getting their settlement started and had help from…]]></description> | |
<link>https://kentcdodds.com/blog/how-gratitude-can-make-you-a-better-developer</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-gratitude-can-make-you-a-better-developer</guid> | |
<pubDate>Mon, 03 Dec 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>This week in the United States we celebrate | |
<a href="https://en.wikipedia.org/wiki/Thanksgiving">Thanksgiving Day</a>. In the US, the | |
celebration was started by Pilgrims who came to America in the 1620s on | |
<a href="https://en.wikipedia.org/wiki/Mayflower">the Mayflower</a>. They had a pretty | |
rough time getting their settlement started and had help from the Native | |
Americans. Together they celebrated and gave thanks to God for a good harvest | |
with a feast and that yearly tradition became the Thanksgiving holiday we | |
celebrate in the US today.</p><p>Each year, Thanksgiving gets more and more overshadowed with the commercialism | |
of Christmas. I want to take this opportunity to share with you some of the | |
things I&#x27;m grateful for and also explain why I think that gratitude can help you | |
be a better software engineer and a better person.</p><p>When I was off | |
<a href="https://twitter.com/kentcdodds/status/1043644565041819648">riding horses</a> at a | |
family reunion last summer, my older brother gave a presentation about attitude | |
and gratitude.</p><p>He presented this graphic:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/31f1388624e40846c9ba88e23e3f6e81/8ff1e/0.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:40%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="The Wheel of Experience: Positive" title="The Wheel of Experience: Positive" src="https://kentcdodds.com/static/31f1388624e40846c9ba88e23e3f6e81/8ff1e/0.png" srcSet="https://kentcdodds.com/static/31f1388624e40846c9ba88e23e3f6e81/f4a45/0.png 259w,https://kentcdodds.com/static/31f1388624e40846c9ba88e23e3f6e81/ef0f6/0.png 518w,https://kentcdodds.com/static/31f1388624e40846c9ba88e23e3f6e81/8ff1e/0.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><figcaption>The Wheel of Experience: Positive</figcaption><p>Given any circumstance (which we often can&#x27;t control), whether those | |
circumstances are positive or negative, we can control what we think of those | |
circumstances, and that impacts the outcome. Positive thoughts lead to positive | |
feelings, actions, and results.</p><p>Then he presented this alternative graphic:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/a152bb241c9651d5ef66d2a66647ab57/8ff1e/1.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:55.99999999999999%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="The Wheel of Experience: Negative" title="The Wheel of Experience: Negative" src="https://kentcdodds.com/static/a152bb241c9651d5ef66d2a66647ab57/8ff1e/1.png" srcSet="https://kentcdodds.com/static/a152bb241c9651d5ef66d2a66647ab57/f4a45/1.png 259w,https://kentcdodds.com/static/a152bb241c9651d5ef66d2a66647ab57/ef0f6/1.png 518w,https://kentcdodds.com/static/a152bb241c9651d5ef66d2a66647ab57/8ff1e/1.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><figcaption>The Wheel of Experience: Negative</figcaption><p>Here we have the same circumstances but respond with negative thoughts which | |
lead to negative feelings, followed by negative actions and then negative | |
results.</p><p>What&#x27;s the big difference between these two experiences? Here we have them | |
together:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/8711900eacb65744b556486964607bd9/8ff1e/2.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:48.75000000000001%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="The Wheel of Experience: Both — Attitude" title="The Wheel of Experience: Both — Attitude" src="https://kentcdodds.com/static/8711900eacb65744b556486964607bd9/8ff1e/2.png" srcSet="https://kentcdodds.com/static/8711900eacb65744b556486964607bd9/f4a45/2.png 259w,https://kentcdodds.com/static/8711900eacb65744b556486964607bd9/ef0f6/2.png 518w,https://kentcdodds.com/static/8711900eacb65744b556486964607bd9/8ff1e/2.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><figcaption>The Wheel of Experience: Both — Attitude</figcaption><p>The turning point for both of these experiences is our thoughts. Our attitude | |
can help guide our thoughts automatically in a positive direction so we more | |
often wind up with positive feelings, actions, and results.</p><p>Let&#x27;s try to make this more concrete. Imagine that your product manager comes to | |
you and says you have to stop experimenting with GraphQL because a new | |
requirement came in and it&#x27;s critical to the business that everyone work on it. | |
That&#x27;s a circumstance you can&#x27;t control, but your attitude and what you think | |
about that circumstance can influence the result.</p><p>Let&#x27;s take the negative route first. You think: &quot;I&#x27;ll bet that manager is just | |
making this up. They don&#x27;t want us to take anymore time to learn GraphQL because | |
they never wanted to invest in it anyway.&quot; 😠 This clearly will make you feel | |
resentful and upset at the product manager and the work they&#x27;ve given you to do. | |
Because of these negative feelings, your behavior toward your manager and | |
co-workers is cold and bitter, and you make short-cuts on the work you&#x27;ve been | |
given because you don&#x27;t want to do it anyway and you think it&#x27;s just something | |
your manager made up because they&#x27;re petty. 😡 The result is a poorly thought | |
out solution that&#x27;s not tested, ends up causing more problems, is code nobody | |
wants to touch, and it&#x27;s a spiral down from there. 🌀</p><p>Now with the positive attitude, things change. You think: &quot;Dang, I really wanted | |
to experiment with GraphQL and my branch will probably fall out of date, but | |
that&#x27;s ok, I&#x27;ll probably be able to get it back up to date when I&#x27;m done with | |
this.&quot; 😃 You get to work on the assignment you&#x27;ve been given, hopeful that you | |
can finish it and get back to your GraphQL work, but also with the positive | |
attitude you approach all your work because you care about software craft and | |
want to make sure you don&#x27;t have to deal with this when it&#x27;s shipped. 🚢 The | |
result is a well crafted solution that&#x27;s well tested and reviewed, you don&#x27;t | |
have any problems when shipping to production so nobody needs to touch the code | |
anyway, you get back to your GraphQL work and turns out it&#x27;s a smashing success | |
and you&#x27;re promoted! 🥇 Good job! 👏</p><p>This is a contrived example, but hopefully you can see this as something that | |
could actually happen and you can see examples of times things have worked out | |
this way for you in the past (both negative and positive).</p><p>It&#x27;s important to realize that &quot;positive results&quot; does not mean you&#x27;ll | |
necessarily get the promotion like we have in this story, that&#x27;s another thing | |
that&#x27;s not entirely in your control. But one thing that is always in your | |
control are the attitude and thoughts you have given any circumstances.</p><p>After my brother explained this (though he didn&#x27;t share that story, I made that | |
up on my own 😉), he showed us a clip from this video (I recommend you watch it | |
before continuing, but please... do come back 😅):</p><figcaption>The Gratitude Experiment</figcaption><p>So science 👩🔬🔬📊⚗️👨🔬 tells us that gratitude makes us happier! The video gives | |
a challenge, and this is the slide my brother showed us for his &quot;Happiness | |
Invitation&quot;</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/a4cea0e0bd841403434b30a48f448d49/8ff1e/3.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:71.00000000000001%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Happiness Invitation" title="Happiness Invitation" src="https://kentcdodds.com/static/a4cea0e0bd841403434b30a48f448d49/8ff1e/3.png" srcSet="https://kentcdodds.com/static/a4cea0e0bd841403434b30a48f448d49/f4a45/3.png 259w,https://kentcdodds.com/static/a4cea0e0bd841403434b30a48f448d49/ef0f6/3.png 518w,https://kentcdodds.com/static/a4cea0e0bd841403434b30a48f448d49/8ff1e/3.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>Step 1</p><ul><li>Journal for 1 week</li><li>5 minutes each night</li><li>&quot;Things I am grateful for&quot; (People, places, things...)</li></ul><p>Step 2</p><ul><li>Look at your list and think &quot;Who gave this to me?&quot;</li><li>Express sincere gratitude to someone else (Letter; Private, personal | |
opportunity)</li></ul><p>Step 3</p><ul><li>After expressing gratitude, write down in detail how you feel now</li></ul><h3>Conclusion</h3><p>One thing that I like about the video and this challenge is that it gives a path | |
for developing a better attitude. I think often our thoughts are automatic and | |
unless we&#x27;re really trying, our brain with start thinking for us. This is where | |
<strong>mindfulness</strong> kicks in. Being mindful of yourself and how you&#x27;re feeling can | |
actually lead you to retraining your brain to think positively automatically. It | |
may be hard to change your thought process today, but given enough time and | |
effort, you can retrain your brain to do what will really bring you happiness.</p><p>I hope you join me this Thanksgiving in the happiness invitation. Tweet with the | |
hashtag | |
<a href="https://twitter.com/hashtag/HappinessInvitation"><strong>#HappinessInvitation</strong></a> | |
every time you do one of the steps (write in your journal, express gratitude, | |
and reflect) and let&#x27;s hold each other accountable. Then next week we&#x27;ll see how | |
we&#x27;re all feeling and hopefully we can land that promotion we&#x27;re looking for | |
😉🥇</p><p>Good luck!</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://github.com/szarouski/lerna-wizard">lerna-wizard</a>: I&#x27;ve been using | |
lerna and this helped me learn what I can do with the commands. Thanks | |
<a href="https://twitter.com/webuniverseio">Sergey Zarouski</a>!</li><li><a href="https://github.com/alexanderson1993/react-konami-hook">useKonami</a>: A custom | |
hook by <a href="https://twitter.com/ralex1993">R. Alex Anderson</a> for supporting | |
Konami code to your app 😍</li><li><a href="https://youtu.be/pjDOJdMM2eg?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Render Prop by Any Other Name</a> — This | |
is a talk I gave at <a href="https://twitter.com/FrameworkSummit">FrameworkSummit</a> in | |
October with <a href="https://twitter.com/ShortDiv">Divya Sasidharan</a> (Vue) and | |
<a href="https://twitter.com/MannIsaac">Isaac Mann</a> (Angular) that compares similar | |
patterns across the frameworks. (There were other great talks there too, see | |
them on | |
<a href="https://youtube.com/channel/UCUTZdTjqY9ypGfpYWvSHC2w">the Framework Summit YouTube channel</a>)</li><li><a href="https://youtu.be/vhWaMPQhMLQ">React&#x27;s New Defaults — Concurrent React and React Hooks</a> — An | |
interesting talk about the future of React by | |
<a href="https://twitter.com/swyx">Shawn Wang</a>.</li><li><a href="https://youtu.be/zWsZcBiwgVE?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">GDG Salt Lake DevFest 2018: Why React Hooks</a> — A | |
talk by me about why React Hooks are a thing and why I&#x27;m super excited to get | |
them. <a href="https://github.com/kentcdodds/gdg-devfest-2018-react">Demo repo</a>.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-gratitude-can-make-you-a-better-developer">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[useEffect vs useLayoutEffect]]></title> | |
<description><![CDATA[Both of these can be used to do basically the same thing, but they have slightly | |
different use cases. So here are some rules for you to consider when deciding | |
which React Hook to use. useEffect 99% of the time this is what you want to use. When…]]></description> | |
<link>https://kentcdodds.com/blog/useeffect-vs-uselayouteffect</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/useeffect-vs-uselayouteffect</guid> | |
<pubDate>Mon, 26 Nov 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Both of these can be used to do basically the same thing, but they have slightly | |
different use cases. So here are some rules for you to consider when deciding | |
which <a href="https://reactjs.org/hooks">React Hook</a> to use.</p><h3><a href="https://reactjs.org/docs/hooks-reference.html#useeffect">useEffect</a></h3><p>99% of the time this is what you want to use. When hooks are stable and if you | |
refactor any of your class components to use hooks, you&#x27;ll likely move any code | |
from <code>componentDidMount</code>, <code>componentDidUpdate</code>, and <code>componentWillUnmount</code> to | |
<code>useEffect</code>.</p><p><strong>The one catch</strong> is that this runs <em>after</em> react renders your component and | |
ensures that your effect callback does not block browser painting. This differs | |
from the behavior in class components where <code>componentDidMount</code> and | |
<code>componentDidUpdate</code> run synchronously after rendering. It&#x27;s more performant | |
this way and most of the time this is what you want.</p><p>However, if your effect is mutating the DOM (via a DOM node ref) <strong><em>and</em></strong> the | |
DOM mutation will change the appearance of the DOM node between the time that it | |
is rendered and your effect mutates it, then you <strong>don&#x27;t</strong> want to use | |
<code>useEffect</code>. You&#x27;ll want to use <code>useLayoutEffect</code>. Otherwise the user could see | |
a flicker when your DOM mutations take effect. <strong>This is pretty much the only | |
time you want to avoid <code>useEffect</code> and use <code>useLayoutEffect</code> instead.</strong></p><h3><a href="https://reactjs.org/docs/hooks-reference.html#uselayouteffect">useLayoutEffect</a></h3><p>This runs synchronously immediately after React has performed all DOM mutations. | |
This can be useful if you need to make DOM measurements (like getting the scroll | |
position or other styles for an element) and then make DOM mutations <strong>or</strong> | |
trigger a synchronous re-render by updating state.</p><p>As far as scheduling, this works the same way as <code>componentDidMount</code> and | |
<code>componentDidUpdate</code>. Your code runs immediately after the DOM has been updated, | |
but before the browser has had a chance to &quot;paint&quot; those changes (the user | |
doesn&#x27;t actually see the updates until after the browser has repainted).</p><h3>Summary</h3><ul><li><strong>useLayoutEffect:</strong> If you need to mutate the DOM and/or DO need to perform | |
measurements</li><li><strong>useEffect:</strong> If you don&#x27;t need to interact with the DOM at all or your DOM | |
changes are unobservable (seriously, most of the time you should use this).</li></ul><h3>Conclusion</h3><p><a href="https://youtu.be/0jlTw2XI7I8?t=39s&amp;list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">I am extremely excited about React&#x27;s upcoming hooks feature</a>. | |
I think it&#x27;s going to make React much easier to learn and use.</p><p><strong>Learn more about React from me</strong>: | |
<a href="http://kcd.im/hooks-and-suspense">Hooks &amp; Suspense Playlist on egghead.io</a> — A | |
free 35 minute list of videos demoing how to use the new React Hooks and | |
Suspense features. (Note: these features are still pretty alpha and likely to | |
change).</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://usehooks.com">useHooks.com</a> — One new React Hook recipe every day. | |
Really cool resource by <a href="https://twitter.com/gabe_ragland">Gabe Ragland</a>.</li><li><a href="https://github.com/sw-yx/fresh-concurrent-react">fresh-concurrent-react</a> by | |
<a href="https://twitter.com/swyx">Shawn Wang</a> — an as up-to-date-as-possible resource | |
about the upcoming features in concurrent react! Really helpful if you want to | |
play around with Suspense and Concurrent React.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/useeffect-vs-uselayouteffect">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Testing Implementation Details]]></title> | |
<description><![CDATA[Last year when I was using enzyme (like everyone else at the time), I stepped | |
carefully around certain APIs in enzyme. I | |
completely avoided shallow rendering , | |
never used APIs like instance() , state() , or find('ComponentName') . And | |
in code…]]></description> | |
<link>https://kentcdodds.com/blog/testing-implementation-details</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/testing-implementation-details</guid> | |
<pubDate>Tue, 20 Nov 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Last year when I was using enzyme (like everyone else at the time), I stepped | |
carefully around certain APIs in enzyme. I | |
<a href="https://kentcdodds.com/blog/why-i-never-use-shallow-rendering">completely avoided shallow rendering</a>, | |
<em>never</em> used APIs like <code>instance()</code>, <code>state()</code>, or <code>find(&#x27;ComponentName&#x27;)</code>. And | |
in code reviews of other people&#x27;s pull requests I explained again and again why | |
it&#x27;s important to avoid these APIs. The reason is they each allow your test to | |
test implementation details of your components. People often ask me what I mean | |
by &quot;implementation details.&quot; I mean, it&#x27;s hard enough to test as it is! Why do | |
we have to make all these rules to make it harder?</p><h3>Why is testing implementation details bad?</h3><p>There are two distinct reasons that it&#x27;s important to avoid testing | |
implementation details. Tests which test implementation details:</p><ol><li>Can break when you refactor application code. <strong>False negatives</strong></li><li>May not fail when you break application code. <strong>False positives</strong></li></ol><p>Let&#x27;s take a look at each of these in turn, using the following simple accordion | |
component as an example:</p><pre><code class="language-jsx">// accordion.js | |
import React from &#x27;react&#x27; | |
import AccordionContents from &#x27;./accordion-contents&#x27; | |
class Accordion extends React.Component { | |
state = {openIndex: 0} | |
setOpenIndex = openIndex =&gt; this.setState({openIndex}) | |
render() { | |
const {openIndex} = this.state | |
return ( | |
&lt;div&gt; | |
{this.props.items.map((item, index) =&gt; ( | |
&lt;&gt; | |
&lt;button onClick={() =&gt; this.setOpenIndex(index)}&gt; | |
{item.title} | |
&lt;/button&gt; | |
{index === openIndex ? ( | |
&lt;AccordionContents&gt;{item.contents}&lt;/AccordionContents&gt; | |
) : null} | |
&lt;/&gt; | |
))} | |
&lt;/div&gt; | |
) | |
} | |
} | |
export default Accordion | |
</code></pre><p>And here&#x27;s a test that tests implementation details:</p><pre><code class="language-jsx">// __tests__/accordion.enzyme.js | |
import React from &#x27;react&#x27; | |
// if you&#x27;re wondering why not shallow, | |
// then please read https://kcd.im/shallow | |
import Enzyme, {mount} from &#x27;enzyme&#x27; | |
import EnzymeAdapter from &#x27;enzyme-adapter-react-16&#x27; | |
import Accordion from &#x27;../accordion&#x27; | |
// Setup enzyme&#x27;s react adapter | |
Enzyme.configure({adapter: new EnzymeAdapter()}) | |
test(&#x27;setOpenIndex sets the open index state properly&#x27;, () =&gt; { | |
const wrapper = mount(&lt;Accordion items={[]} /&gt;) | |
expect(wrapper.state(&#x27;openIndex&#x27;)).toBe(0) | |
wrapper.instance().setOpenIndex(1) | |
expect(wrapper.state(&#x27;openIndex&#x27;)).toBe(1) | |
}) | |
test(&#x27;Accordion renders AccordionContents with the item contents&#x27;, () =&gt; { | |
const hats = {title: &#x27;Favorite Hats&#x27;, contents: &#x27;Fedoras are classy&#x27;} | |
const footware = { | |
title: &#x27;Favorite Footware&#x27;, | |
contents: &#x27;Flipflops are the best&#x27;, | |
} | |
const wrapper = mount(&lt;Accordion items={[hats, footware]} /&gt;) | |
expect(wrapper.find(&#x27;AccordionContents&#x27;).props().children).toBe(hats.contents) | |
}) | |
</code></pre><p>Raise your hand if you&#x27;ve seen (or written) tests like this in your codebase | |
(🙌).</p><p>Ok, now let&#x27;s take a look at how things break down with these tests...</p><h3>False negatives when refactoring</h3><p>A surprising number of people find testing distasteful, especially UI testing. | |
Why is this? There are various reasons for it, but one big reason I hear again | |
and again is that people spend way too much time babysitting the tests. &quot;Every | |
time I make a change to the code, the tests break!&quot; This is a real drag on | |
productivity! Let&#x27;s see how our tests fall prey to this frustrating problem.</p><p>Let&#x27;s say I come in and I&#x27;m refactoring this accordion to prepare it to allow | |
for multiple accordion items to be open at once. A refactor doesn&#x27;t change | |
existing behavior at all, it just changes the <strong>implementation</strong>. So let&#x27;s | |
change the <strong>implementation</strong> in a way that doesn&#x27;t change the behavior:</p><pre><code class="language-diff">class Accordion extends React.Component { | |
- state = {openIndex: 0} | |
- setOpenIndex = openIndex =&gt; this.setState({openIndex}) | |
+ state = {openIndexes: [0]} | |
+ setOpenIndex = openIndex =&gt; this.setState({openIndexes: [openIndex]}) | |
render() { | |
- const {openIndex} = this.state | |
+ const {openIndexes} = this.state | |
return ( | |
&lt;div&gt; | |
{this.props.items.map((item, index) =&gt; ( | |
&lt;&gt; | |
&lt;button onClick={() =&gt; this.setOpenIndex(index)}&gt; | |
{item.title} | |
&lt;/button&gt; | |
- {index === openIndex ? ( | |
+ {openIndexes.includes(index) ? ( | |
&lt;AccordionContents&gt;{item.contents}&lt;/AccordionContents&gt; | |
) : null} | |
&lt;/&gt; | |
))} | |
&lt;/div&gt; | |
) | |
} | |
} | |
</code></pre><p>Awesome, we do a quick check in the app and everything&#x27;s still working properly, | |
so when we come to this component later to support opening multiple accordions, | |
it&#x27;ll be a cinch! Then we run the tests and 💥kaboom💥 they&#x27;re busted. Which one | |
broke? <code>setOpenIndex sets the open index state properly</code>.</p><p>What&#x27;s the error message?</p><pre><code>expect(received).toBe(expected) | |
Expected value to be (using ===): | |
0 | |
Received: | |
undefined | |
</code></pre><p>Is that test failure warning us of a real problem? Nope! The component still | |
works fine.</p><p><strong>This is what&#x27;s called a false negative.</strong> It means that we got a test failure, | |
but it was because of a broken test, not broken app code. I honestly cannot | |
think of a more annoying test failure situation. Oh well, let&#x27;s go ahead and fix | |
our test:</p><pre><code class="language-diff"> test(&#x27;setOpenIndex sets the open index state properly&#x27;, () =&gt; { | |
const wrapper = mount(&lt;Accordion items={[]} /&gt;) | |
- expect(wrapper.state(&#x27;openIndex&#x27;)).toEqual(0) | |
+ expect(wrapper.state(&#x27;openIndexes&#x27;)).toEqual([0]) | |
wrapper.instance().setOpenIndex(1) | |
- expect(wrapper.state(&#x27;openIndex&#x27;)).toEqual(1) | |
+ expect(wrapper.state(&#x27;openIndexes&#x27;)).toEqual([1]) | |
}) | |
</code></pre><p>The takeaway: Tests which test implementation details can give you a false | |
negative when you refactor your code. This leads to brittle and frustrating | |
tests that seem to break anytime you so much as look at the code.</p><h3>False positives</h3><p>Ok, so now let&#x27;s say your co-worker is working in the Accordion and they see | |
this code:</p><pre><code class="language-jsx">&lt;button onClick={() =&gt; this.setOpenIndex(index)}&gt;{item.title}&lt;/button&gt; | |
</code></pre><p>Immediately their premature performance optimization feelings kick in and they | |
say to themselves, &quot;hey! inline arrow functions in <code>render</code> are | |
<a href="https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578">bad for performance</a>, | |
so I&#x27;ll just clean that up! I think this should work, I&#x27;ll just change it really | |
quick and run tests.&quot;</p><pre><code class="language-jsx">&lt;button onClick={this.setOpenIndex}&gt;{item.title}&lt;/button&gt; | |
</code></pre><p>Cool. Run the tests and... ✅✅ awesome! They commit the code without checking | |
it in the browser because tests give confidence right? That commit goes in a | |
completely unrelated PR that changes thousands of lines of code and is | |
understandably missed. The accordion breaks in production and Nancy is unable to | |
get her tickets to see | |
<a href="https://www.broadway-at-the-eccles.com/events/wicked">Wicked in Salt Lake next February</a>. | |
Nancy is crying and your team feels horrible.</p><p>So what went wrong? Didn&#x27;t we have a test to verify that the state changes when | |
<code>setOpenIndex</code> is called <em>and</em> that the accordion contents are displayed | |
appropriately!? Yes you did! But the problem is that there was no test to verify | |
that the button was wired up to <code>setOpenIndex</code> correctly.</p><p><strong>This is called a false positive.</strong> It means that we didn&#x27;t get a test failure, | |
but we should have! So how do we cover ourselves to make sure this doesn&#x27;t | |
happen again? We need to add another test to verify clicking the button updates | |
the state correctly. And then I need to add a coverage threshold of 100% code | |
coverage so we don&#x27;t make this mistake again. Oh, and I should write a dozen or | |
so ESLint plugins to make sure people don&#x27;t use these APIs that encourage | |
testing implementation details!</p><p>... But I&#x27;m not going to bother... Ugh, I&#x27;m just so tired of all these false | |
positives and negatives, I&#x27;d almost rather not write tests at all. DELETE ALL | |
THE TESTS! Wouldn&#x27;t it be nice if we had a tool that had a wider | |
<a href="https://twitter.com/kentcdodds/status/859994199738900480">pit</a> of | |
<a href="https://blog.codinghorror.com/falling-into-the-pit-of-success">success</a>? Yes it | |
would! And guess what, we DO have such a tool!</p><h3>Implementation detail free testing</h3><p>So we could rewrite all these tests with enzyme, limiting ourselves to APIs that | |
are free of implementation details, but instead, I&#x27;m just going to use | |
<a href="https://github.com/testing-library/react-testing-library">react-testing-library</a> | |
which will make it very difficult to include implementation details in my tests. | |
Let&#x27;s check that out now!</p><pre><code class="language-jsx">// __tests__/accordion.rtl.js | |
import React from &#x27;react&#x27; | |
import {render, fireEvent} from &#x27;react-testing-library&#x27; | |
import Accordion from &#x27;../accordion&#x27; | |
test(&#x27;can open accordion items to see the contents&#x27;, () =&gt; { | |
const hats = {title: &#x27;Favorite Hats&#x27;, contents: &#x27;Fedoras are classy&#x27;} | |
const footware = { | |
title: &#x27;Favorite Footware&#x27;, | |
contents: &#x27;Flipflops are the best&#x27;, | |
} | |
const {getByText, queryByText} = render( | |
&lt;Accordion items={[hats, footware]} /&gt;, | |
) | |
expect(getByText(hats.contents)).toBeInTheDocument() | |
expect(queryByText(footware.contents)).toBeNull() | |
fireEvent.click(getByText(footware.title)) | |
expect(getByText(footware.contents)).toBeInTheDocument() | |
expect(queryByText(hats.contents)).toBeNull() | |
}) | |
</code></pre><p>Sweet! A single test that verifies all the behavior really well. And this test | |
passes whether my state is called <code>openIndex</code>, <code>openIndexes</code>, or <code>tacosAreTasty</code> | |
🌮. Nice! Got rid of that false negative! And if I wire up my click handler | |
incorrectly, this test will fail. Sweet, got rid of that false positive too! And | |
I didn&#x27;t have to memorize any list of rules or install a bunch of annoying | |
ESLint plugins. I just use the tool in the typical usage, and I get a test that | |
actually can give me confidence my accordion is working as the user wants it | |
too.</p><h3>So... What are implementation details then?</h3><p>Here&#x27;s the simplest definition I can come up with:</p><blockquote><p><em>Implementation details are things which users of your code will not typically | |
use, see, or even know about.</em></p></blockquote><p>So the first question we need an answer to is: &quot;Who is the user of this code.&quot; | |
Well, the end user who will be interacting with our component in the browser is | |
definitely a user. They&#x27;ll be observing and interacting with the rendered | |
buttons and contents. But we also have the developer who will be rendering the | |
accordion with props (in our case, a given list of items). So React components | |
typically have two users: end-users, and developers. <strong>End-users and developers | |
are the two &quot;users&quot; that our application code needs to consider.</strong></p><p>Great, so what parts of our code do each of these users use, see, and know | |
about? The end user will see/interact with what we render in the <code>render</code> | |
method. The developer will see/interact with the props they pass to the | |
component. So our test should typically only see/interact with the props that | |
are passed, and the rendered output.</p><p>This is precisely what the | |
<a href="https://github.com/testing-library/react-testing-library">react-testing-library</a> | |
test does. It passes fake props to the Accordion, then it interacts with the | |
rendered output by querying the output for the contents that will be displayed | |
to the user (or ensuring that it wont be displayed) and clicking the buttons | |
that are rendered.</p><p>Now consider the enzyme test. With enzyme, we access the <code>state</code> of <code>openIndex</code>. | |
This is not something that either of our users care about directly. They don&#x27;t | |
know that&#x27;s what it&#x27;s called, they don&#x27;t know whether the open index is stored | |
as a single primitive value, or stored as an array, and frankly they don&#x27;t care. | |
They also don&#x27;t know or care about the <code>setOpenIndex</code> method specifically. And | |
yet, our test knows about both of these implementation details.</p><p>This is what makes our enzyme test prone to false negatives. Because <strong>by making | |
our test use the component differently than end-users and developers do, we | |
create a third user our application code needs to consider: the tests!</strong> And | |
frankly, the tests are one user that nobody cares about. I don&#x27;t want my | |
application code to consider the tests. What a complete waste of time. I don&#x27;t | |
want tests that are written for their own sake. <em>Automated tests should verify | |
that the application code works for the production users.</em></p><blockquote><p><em><a href="https://twitter.com/kentcdodds/status/977018512689455106">The more your tests resemble the way your software is used, the more confidence they can give you.</a> | |
— me</em></p><p>Read more about this in <a href="https://kentcdodds.com/blog/avoid-the-test-user">Avoid the Test User</a>.</p></blockquote><p>Oh, and <a href="https://reactjs.org/hooks">React Hooks</a> got you all excited? If you | |
rewrite that accordion component to use React hooks, the enzyme test fails | |
terribly, while the | |
<a href="https://github.com/testing-library/react-testing-library">react-testing-library</a> | |
test continues to work.</p><p><img src="https://kentcdodds.com/0-11dc2676f0b2705b5aabf1fe925b8c51.gif" alt="happy goats"/></p><h3>Conclusion</h3><p>So how do you avoid testing implementation details? Using the right tools is a | |
good start. A few weeks ago I sent this process for how to know what to test, | |
following this process helps you have the right mindset when testing and you | |
will naturally avoid implementation details:</p><ol><li>What part of your untested codebase would be really bad if it broke? (The | |
checkout process)</li><li>Try to narrow it down to a unit or a few units of code (When clicking the | |
&quot;checkout&quot; button a request with the cart items is sent to /checkout)</li><li>Look at that code and consider who the &quot;users&quot; are (The developer rendering | |
the checkout form, the end user clicking on the button)</li><li>Write down a list of instructions for that user to manually test that code | |
to make sure it&#x27;s not broken. (render the form with some fake data in the | |
cart, click the checkout button, ensure the mocked /checkout API was called | |
with the right data, respond with a fake successful response, make sure the | |
success message is displayed).</li><li>Turn that list of instructions into an automated test.</li></ol><p>I hope that&#x27;s helpful to you! If you really want to take your testing to the | |
next level, then I definitely recommend you get a Pro license for | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>🏆</p><p>Good luck!</p><p>P.S. If you&#x27;d like to play around with all this, | |
<a href="https://codesandbox.io/s/rlnw1r5nxo">here&#x27;s a codesandbox</a>.</p><p>P.S.P.S. As an exercise for you... What happens to that second enzyme test if I | |
change the name of the <code>AccordionContents</code> component? {insert biggest eye roll | |
ever}</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/testing-implementation-details">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Common Testing Mistakes]]></title> | |
<description><![CDATA[Mistake Number 0 One of the biggest mistakes you could make would be missing out on my full | |
Testing JS course . (see what I did there?) Mistake Number 1: Testing Implementation Details I harp on this a lot ( read more ). It's | |
because it's a huge…]]></description> | |
<link>https://kentcdodds.com/blog/common-testing-mistakes</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/common-testing-mistakes</guid> | |
<pubDate>Mon, 12 Nov 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<h3>Mistake Number 0</h3><p>One of the biggest mistakes you could make would be missing out on my full | |
<a href="https://testingjavascript.com">Testing JS course</a>. (see what I did there?)</p><h3>Mistake Number 1: Testing Implementation Details</h3><p>I harp on this a lot (<a href="https://kentcdodds.com/blog/testing-implementation-details">read more</a>). It&#x27;s | |
because it&#x27;s a huge problem in testing and leads to tests that don&#x27;t give nearly | |
as much confidence as they could. Here&#x27;s a very simple example of a test that&#x27;s | |
testing implementation details:</p><pre><code class="language-jsx">// counter.js | |
import React from &#x27;react&#x27; | |
export class Counter extends React.Component { | |
state = {count: 0} | |
increment = () =&gt; this.setState(({count}) =&gt; ({count: count + 1})) | |
render() { | |
const {count} = this.state | |
return &lt;button onClick={this.increment}&gt;{count}&lt;/button&gt; | |
} | |
} | |
// __tests__/counter.js | |
import React from &#x27;react&#x27; | |
// (it&#x27;s hard to test implementation details with react-testing-library, | |
// so we&#x27;ll use enzyme in this example 😅) | |
import {mount} from &#x27;enzyme&#x27; | |
import {Counter} from &#x27;../counter&#x27; | |
test(&#x27;the increment method increments count&#x27;, () =&gt; { | |
const wrapper = mount(&lt;Counter /&gt;) | |
// don&#x27;t ever do this: | |
expect(wrapper.instance().state.count).toBe(0) | |
wrapper.instance().increment() | |
expect(wrapper.instance().state.count).toBe(1) | |
}) | |
</code></pre><p>So why is this testing implementation details? Why is it so bad to test | |
implementation details? Here are two truths about tests that focus on | |
implementation details like the test above:</p><ol><li>I can break the code and not the test (eg: I could make a typo in my | |
button&#x27;s onClick assignment)</li><li>I can refactor the code and break the test (eg: I could rename increment to | |
updateCount)</li></ol><p>These kinds of tests are the worst to maintain because you&#x27;re constantly | |
updating them (due to point #2), and they don&#x27;t even give you solid confidence | |
(due to point #1).</p><p>In <a href="https://testingjavascript.com">my course</a> I&#x27;ll show you the right way to | |
write tests and avoid this common mistake.</p><h3>Mistake Number 2: 100% code coverage</h3><p>Trying to go for 100% code coverage for an application is a total mistake and I | |
see this all the time. Interestingly I&#x27;ve normally seen this as a mandate from | |
management, but wherever it&#x27;s coming from it&#x27;s coming out of a misunderstanding | |
of what a code coverage report can and cannot tell you about the confidence you | |
can have in your codebase.</p><p>What code coverage is telling you:</p><ul><li>This code was run when your tests were run.</li></ul><p>What code coverage is NOT telling you:</p><ul><li>This code will work according to the business requirements.</li><li>This code works with all the other code in the application.</li><li>The application cannot get into a bad state</li></ul><p>Another problem with code coverage reports is that every line of covered code | |
adds just as much to the overall coverage report as any other line. What this | |
means is that you can increase your code coverage just as much by adding tests | |
to your &quot;About us&quot; page as you can by adding tests to your &quot;Checkout&quot; page. One | |
of those things is more important than the other, and code coverage can&#x27;t give | |
you any insight into that for your codebase...</p><p>There&#x27;s no one-size-fits-all solution for a good code coverage number to shoot | |
for. Every application&#x27;s needs are different. I concern myself less with the | |
code coverage number and more with how confident I am that the important parts | |
of my application are covered. I use the code coverage report to help me <em>after</em> | |
I&#x27;ve already identified which parts of my application code are critical. It | |
helps me to know if I&#x27;m missing some edge cases the code is covering but my | |
tests are not.</p><blockquote><p><em>I should note that for open source modules, going for 100% code coverage is | |
totally appropriate because they&#x27;re generally a lot easier to keep at 100% | |
(because they&#x27;re smaller and more isolated) and they&#x27;re really important code | |
due to the fact that they&#x27;re shared in multiple projects.</em></p></blockquote><p>I talked a bit about this in | |
<a href="https://youtu.be/O2tsvUJT09U?index=9&amp;list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u&amp;t=0s">my livestream</a> | |
the other day, check it out!</p><h3>Mistake Number 3: Repeat Testing</h3><p>One of the biggest complaints people have about end-to-end (E2E) tests is how | |
slow and brittle they are when compared to integration or unit tests. There&#x27;s no | |
way you&#x27;ll ever get a single E2E test as fast or reliable as a single unit test. | |
It&#x27;s just never going to happen. That said a single E2E test will get you WAY | |
more confidence than a single unit test. In fact, there are some corners of | |
confidence that are impossible to get out of unit tests that E2E tests are great | |
at, so it&#x27;s definitely worth having them around!</p><p>But this doesn&#x27;t mean that we can&#x27;t make our E2E tests faster and more reliable | |
than you&#x27;ve likely experienced in the past. Repeat testing is a common mistake | |
that people make when writing E2E tests that contribute to the poor performance | |
and reliability.</p><p><a href="https://kentcdodds.com/blog/test-isolation-with-react">Tests should always work in isolation</a>. So | |
that means every test should be executed as a different user. So every test will | |
need to register and login as a brand new user right? Right. So you need to have | |
a few page objects for the registration and login pages because you&#x27;ll be | |
running through those pages in every test right? WRONG! That&#x27;s the mistake!</p><p>Let&#x27;s take a step back. Why are you writing tests? So you can ship your | |
application with confidence that things wont break! Let&#x27;s say you have 100 tests | |
that need an authenticated user. How many times do you need to run through the | |
&quot;happy path&quot; registration flow to be confident that flow works? 100 times or 1 | |
time? I think it&#x27;s safe to say that if it worked once, it should work every | |
time. So those 99 extra runs don&#x27;t give you any extra confidence. <strong>That&#x27;s | |
wasted effort.</strong></p><p>So what do you do instead? I mean, we already established that your tests should | |
work in isolation so you shouldn&#x27;t be sharing a user between them. Here&#x27;s what | |
you do: make the same HTTP calls in your tests that your application makes when | |
you register and log in a new user! Those requests will be MUCH faster than | |
clicking and typing around the page and there&#x27;s less of a chance for false | |
negative failures. And as long as you keep one test around that actually <em>does</em> | |
test the registration/login flow you haven&#x27;t lost any confidence that this flow | |
works.</p><h3>Conclusion</h3><p>Always remember the reason that you&#x27;re testing is about confidence. If something | |
your test is doing isn&#x27;t bringing you more confidence, then consider whether you | |
can stop doing it!</p><p>Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/common-testing-mistakes">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[UI Testing Myths]]></title> | |
<description><![CDATA[Myth 1: "Tests always break when I make any changes to the code" This is actually a truth... if the tests are written incorrectly. If your test | |
is testing implementation details, then of course they'll break when the | |
implementation changes! But your…]]></description> | |
<link>https://kentcdodds.com/blog/ui-testing-myths</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/ui-testing-myths</guid> | |
<pubDate>Thu, 08 Nov 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<h3>Myth 1: &quot;Tests always break when I make any changes to the code&quot;</h3><p>This is actually a truth... if the tests are written incorrectly. If your test | |
is testing implementation details, then of course they&#x27;ll break when the | |
implementation changes! But your user doesn&#x27;t care about the implementation | |
details. In fact, they don&#x27;t even care whether you&#x27;re using React, Angular, or | |
jQuery. So for the most part, your tests shouldn&#x27;t care about that either. 💯</p><p>Unfortunately, many tools out there encourage testing implementation details. Do | |
that and you&#x27;ll often wind up rewriting tests. &quot;Why am I even testing this!?&quot; | |
you&#x27;ll ask yourself, and I don&#x27;t blame you. That&#x27;s why on | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a> I show you how test the | |
<em>right</em> way.</p><h3>Myth 2: &quot;I can&#x27;t test a &#x27;connected&#x27; redux component&quot;</h3><p>The conventional wisdom of testing components that use Redux is that you should | |
test the component in isolation from Redux, and then test the Redux action | |
creators and reducers separately.</p><p>But if you do this, your tests can&#x27;t give you any confidence that your | |
components communicate properly <em>with</em> Redux.</p><p>Instead, you can actually test your connected component with your real Redux | |
store. Do this, and you&#x27;ll get the confidence that your component is rendering | |
properly, <em>and</em> that the Redux action creators and reducers are all working | |
together in tandem. Just like they will in production. ✅</p><p>On <a href="https://testingjavascript.com">TestingJavaScript.com</a>, I show you how to | |
test in this way. The same concepts apply for React Router 🔀 and other | |
providers (like the Theme Provider from <a href="https://emotion.sh">emotion</a> 👩🎤), and | |
the course will show how to apply this method to those, too!</p><h3>Myth 3: &quot;End-to-End tests are slow and brittle&quot;</h3><p>This, too, can be true if the tests are written incorrectly. A common mistake I | |
see in E2E testing is doing the same things in every test — for instance, every | |
test going through the whole registration and login flow before doing whatever | |
is needed for the test. When you do stuff like this, you start seeing a lot of | |
duplication, and that&#x27;s when you start creating things like &quot;page objects&quot; | |
(which is a poor practice). 😐</p><p>On <a href="https://testingjavascript.com">TestingJavaScript.com</a>, I show you how you | |
can get confidence that the registration and login flows are working, and then | |
skip those for the rest of your tests so you can significantly speed up the | |
tests and reduce the points of failure. When you write tests this way and use | |
tools like | |
<a href="https://github.com/testing-library/cypress-testing-library">cypress-testing-library</a>, | |
practices like page objects are totally unnecessary, and your tests are easier | |
to maintain, more reliable, and run faster. You might even find yourself | |
replacing Chrome with Cypress as your development workflow tool (which I show | |
you how to do in the course as well!) 😱</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/ui-testing-myths">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[The Merits of Mocking]]></title> | |
<description><![CDATA[The more your tests resemble the way your software is used, the more | |
confidence they can give | |
you. — me One of the biggest challenges people face with testing is knowing what to test . | |
There are lots of reasons for that, but one big, flashing-lights…]]></description> | |
<link>https://kentcdodds.com/blog/the-merits-of-mocking</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/the-merits-of-mocking</guid> | |
<pubDate>Mon, 05 Nov 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<blockquote><p><em>The more your tests resemble the way your software is used, the more | |
confidence they can give | |
you. — </em><a href="https://twitter.com/kentcdodds/status/977018512689455106"><em>me</em></a></p></blockquote><p><a href="https://twitter.com/kentcdodds/status/1048645068616163328">One of the biggest challenges people face with testing is knowing what to test</a>. | |
There are lots of reasons for that, but one big, flashing-lights reason is | |
mocking. Many people don&#x27;t know when to add a mock version of code or have their | |
test run the actual code directly. These are challenges I&#x27;ll help you work | |
through in the JavaScript Mocking Fundamentals module of my Testing JavaScript | |
course.</p><p><strong>Mocking lets you fake it so you <em>can</em> make it.</strong> If you couldn&#x27;t have a fake | |
version of certain modules or services, testing the checkout process of an app | |
would cost you a lot of money in credit card fees. Talk about paying a high | |
price for confidence! 🤑 So instead, we make a fake version of that credit card | |
charging service to avoid paying the fees.</p><p>But mocking comes with a cost of its own.</p><p><strong>Mocking severs the real-world connection between what you&#x27;re testing and what | |
you&#x27;re mocking.</strong> Even if we have confidence that our code works with our fake | |
version of the credit card service, we can&#x27;t have 100% confidence that our code | |
will work in production with the real version of the credit card service.</p><p><strong>When you mock something, you&#x27;re making a trade-off.</strong> You&#x27;re trading | |
confidence for something else. For me, that something else is usually | |
practicality — meaning I wouldn&#x27;t be able to test this thing at all, or it may | |
be pretty difficult/messy, without mocking. (Like in our credit card example.)</p><p><strong>In my UI unit and integration tests, I have a rule.</strong> I never make actual | |
network calls; instead, I&#x27;ll mock the server response by mocking the module | |
responsible for making the network calls. I&#x27;ll also mock animation libraries to | |
avoid waiting for animations before elements are removed from the page. Other | |
than that, most of my UI tests are using the real production code. For E2E | |
tests, I avoid mocking anything (with the exception of the backend hitting fake | |
or test services and not actual credit card services, for example).</p><p><strong>Saving a few milliseconds per test?</strong> That&#x27;s not a good reason to mock. People | |
like shallow rendering — component mocking to the max — because it&#x27;s faster. | |
That&#x27;s true, but we&#x27;re talking milliseconds faster. If it takes a long time to | |
render your entire component tree, sounds to me like you have a real performance | |
bug in your software that needs to be addressed. I realize that time adds up | |
(50ms per test * 1000 tests = 50 seconds). But the less you mock, the fewer | |
tests you need, and trading confidence for a minute or two faster test suite is | |
a bad trade. 😵</p><p><strong>There&#x27;s a time and a place for mocking.</strong> And when you need to mock, Jest | |
makes it easy with some really sweet mocking utilities. In | |
<a href="https://testingjavascript.com">testingjavascript.com</a> I&#x27;ll show you how to | |
implement some of Jest&#x27;s mocking capabilities in raw node so you can get an idea | |
of what&#x27;s going on. It&#x27;s brilliant. Here&#x27;s an example of simulating Jest&#x27;s | |
inline mock functionality in pure node:</p><pre><code class="language-js">function fn(impl = () =&gt; {}) { | |
const mockFn = (...args) =&gt; { | |
mockFn.mock.calls.push(args) | |
return impl(...args) | |
} | |
mockFn.mock = {calls: []} | |
return mockFn | |
} | |
const utilsPath = require.resolve(&#x27;../utils&#x27;) | |
require.cache[utilsPath] = { | |
id: utilsPath, | |
filename: utilsPath, | |
loaded: true, | |
exports: { | |
getWinner: fn((p1, p2) =&gt; p1), | |
}, | |
} | |
</code></pre><p>Now, any code that requires that utils module will get the mock function version | |
of that module.</p><p>It&#x27;s not quite as capable as Jest&#x27;s inline mocking abilities, but we&#x27;ll cover | |
that in more hands-on detail in the JavaScript Mocking Fundamentals module of | |
the course!</p><p>See you <a href="https://testingjavascript.com">there</a>.</p><p>— Kent.</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://6figuredev.com/podcast/episode-061-react-with-kent-c-dodds">Episode 061 — React with Kent C. Dodds</a> | |
on <a href="https://6figuredev.com">The 6 Figure Developer podcast</a>.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/the-merits-of-mocking">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[The time I messed up]]></title> | |
<description><![CDATA[We all have stories where automated tests could've saved us from disaster, but I | |
want to tell you a story of a time when I went overboard with a certain testing | |
practice and it went really badly for me... This was a few years ago. I was working on…]]></description> | |
<link>https://kentcdodds.com/blog/the-time-i-messed-up</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/the-time-i-messed-up</guid> | |
<pubDate>Mon, 22 Oct 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>We all have stories where automated tests could&#x27;ve saved us from disaster, but I | |
want to tell you a story of a time when I went overboard with a certain testing | |
practice and it went really badly for me...</p><p>This was a few years ago. I was working on | |
<a href="https://github.com/formly-js/angular-formly">angular-formly</a> and I had just | |
discovered React&#x27;s PropTypes feature. I thought it was the coolest thing since | |
sliced bread. lol, I just looked it up and | |
<a href="https://github.com/facebook/react/issues/2206">my first issue</a> on the React | |
repo was asking if we could add an objectWith PropType validator. I wrote the | |
whole thing including tests and put that in the issue. Sophie Alpert responded | |
with:</p><blockquote><p><em>Isn&#x27;t this what React.PropTypes.shape is for?</em></p></blockquote><p>Ha! Whoops! Anyway, I decided it&#x27;d be really cool to have development-only | |
runtime validation on the configuration object that people pass to | |
angular-formly when configuring their forms.</p><p><a href="https://github.com/facebook/react/issues/3079">My second issue</a> on the React | |
repo was asking if the React team would be willing to extract prop types into | |
its own package (this has actually now taken place! Checkout | |
<a href="http://npm.im/prop-types">prop-types</a>). That didn&#x27;t work out, so I decided to | |
write my own package and thus | |
<a href="https://github.com/kentcdodds/api-check">api-check</a> was born!</p><p>One of the cool things about a library like this is that the whole thing is a | |
bunch of pure functions! And we all know that pure functions are super easy to | |
test, right?! Also, because I had a very clear picture of what I wanted to | |
implement (with prop-types as my inspiration) I thought test-driven development | |
would be a synch.</p><p>So I set to work. I started writing a bunch of tests and implemented api-check | |
as I went. Honestly, it was exhilarating. (I&#x27;d say intoxicating, but I&#x27;ve never | |
actually had a drop of alcohol in my life, so I wouldn&#x27;t know 😅). I was having | |
a blast writing this out.</p><p>But as time went on, I started noticing something... It was becoming | |
progressively less fun and more difficult to implement the behaviors as I added | |
them. The library code started getting really complicated. Now I shudder even | |
thinking about it. There&#x27;s no way you could convince me to work on that code | |
anymore.</p><p>So what went wrong? I thought TDD was the process by which you create | |
beautifully and intentionally designed software. But what I was left with is a | |
mess that I don&#x27;t want to touch. I must have done something wrong...</p><p>I did. TDD is a three-step process. It&#x27;s often referred to as the &quot;red, green, | |
refactor cycle&quot;</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:676px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/12bcb6f09fd6ef9bbb497dafd6e73143/5fd36/0.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:97.92899408284025%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="TDD Cycle" title="TDD Cycle" src="https://kentcdodds.com/static/12bcb6f09fd6ef9bbb497dafd6e73143/5fd36/0.jpg" srcSet="https://kentcdodds.com/static/12bcb6f09fd6ef9bbb497dafd6e73143/d2274/0.jpg 259w,https://kentcdodds.com/static/12bcb6f09fd6ef9bbb497dafd6e73143/34151/0.jpg 518w,https://kentcdodds.com/static/12bcb6f09fd6ef9bbb497dafd6e73143/5fd36/0.jpg 676w" sizes="(max-width: 676px) 100vw, 676px"/> | |
</a> | |
</span></p><p>Here&#x27;s the way it works:</p><ul><li>🚨 <strong>Red</strong>: Write a test for the function/module you&#x27;re going to create before | |
it exists/supports the feature you&#x27;re adding. This gives you a test that fails | |
(you get a &quot;red&quot; error message).</li><li>✅ <strong>Green</strong>: Implement just enough code to get that test passing (you get a | |
&quot;green&quot; success message).</li><li>🌀 <strong>Refactor</strong>: Look over the code you have written and refactor it to ensure | |
it&#x27;s well-written, as easy as possible to read/understand, and well-designed. | |
(The cool thing with this step is that you now have a test in place that will | |
tell you if you break something as you refactor).</li><li>🔁 <strong>Repeat</strong>: It&#x27;s a cycle, after all 😉 Keep going until you&#x27;ve finished | |
implementing everything you need to.</li></ul><p>So where did I go wrong with api-check? Well, I kinda skipped the refactor step. | |
I got so excited from the &quot;high&quot; (another thing of which I&#x27;m ignorant) that I | |
felt from turning that red test to a green test that I just continued on with | |
the next feature without stopping to consider if what I was building was at all | |
maintainable. Turns out it wasn&#x27;t. I was building a monstrosity of | |
difficult-to-understand code. The code was legacy the second it left my fingers.</p><h3>Conclusion</h3><p>So, what do we learn from this? Test-driven development is awesome, and it can | |
really help you develop great code in certain situations. But if you skip the | |
refactor step and aren&#x27;t intentional about what you&#x27;re building, it can lead to | |
disaster.</p><p>Be careful out there, friends. And learn to use TDD the right way with me at | |
<a href="https://testingjavascript.com/?utm_source=kcd-list&amp;utm_medium=email&amp;utm_campaign=early-bird">TestingJavaScript.com</a> | |
🏆</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/the-time-i-messed-up">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React is an implementation detail]]></title> | |
<description><![CDATA[I'm so excited about the response to my new | |
Testing JavaScript course! I knew the developer | |
community needed help with testing the right way, but... wow. I'm floored. 🙏 But if you haven't signed up yet (or even if you have), let's talk about…]]></description> | |
<link>https://kentcdodds.com/blog/react-is-an-implementation-detail</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/react-is-an-implementation-detail</guid> | |
<pubDate>Sat, 20 Oct 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I&#x27;m <strong>so excited</strong> about the response to my new | |
<a href="http://testingjavascript.com">Testing JavaScript</a> course! I knew the developer | |
community needed help with testing the right way, but... wow. I&#x27;m floored. 🙏</p><p>But if you <em>haven&#x27;t</em> signed up yet (or even if you have), let&#x27;s talk about React | |
for a minute.</p><p>If you&#x27;ve been following me for a while, you know I&#x27;m pretty excited about | |
React. I&#x27;m most effective with React, and I don&#x27;t use any other frameworks to | |
get work done on the frontend.</p><p>But I&#x27;m also a big fan of avoiding testing implementation details, and <strong>React | |
is an implementation detail!!</strong></p><p>Guess what that means? All the stuff we talk about in | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a> is relatively easy to | |
apply with other frameworks — like whichever framework you&#x27;re using right now, | |
or will use in the future.</p><p>In fact, I have an entire course showing you how to get up and running with your | |
own testing utility and enjoy the same benefits that you&#x27;ll see with | |
react-testing-library. That&#x27;s thanks to the fact that react-testing-library | |
itself is a very small library, and the real brains behind it is | |
dom-testing-library — which is totally framework-agnostic! Cool right!? 😎</p><p>In fact, | |
<a href="https://github.com/kentcdodds/dom-testing-library-with-anything/blob/9361a120bc52334968e94a10363bab9724d5dbd3/jquery.test.js">check out this example</a> | |
from the course for testing a jQuery plugin with dom-testing-library:</p><pre><code class="language-js">import &#x27;jest-dom/extend-expect&#x27; | |
import $ from &#x27;jquery&#x27; | |
import {getQueriesForElement, fireEvent} from &#x27;dom-testing-library&#x27; | |
$.fn.countify = function countify() { | |
this.html(` | |
&lt;div&gt; | |
&lt;button&gt;0&lt;/button&gt; | |
&lt;/div&gt; | |
`) | |
const $button = this.find(&#x27;button&#x27;) | |
$button._count = 0 | |
$button.click(() =&gt; { | |
$button._count++ | |
$button.text($button._count) | |
}) | |
} | |
// tests: | |
test(&#x27;counter increments&#x27;, () =&gt; { | |
const div = document.createElement(&#x27;div&#x27;) | |
$(div).countify() | |
const {getByText} = getQueriesForElement(div) | |
const counter = getByText(&#x27;0&#x27;) | |
fireEvent.click(counter) | |
expect(counter).toHaveTextContent(&#x27;1&#x27;) | |
fireEvent.click(counter) | |
expect(counter).toHaveTextContent(&#x27;2&#x27;) | |
}) | |
</code></pre><p>The other frameworks are even better, considering most modern frameworks are | |
component-based. What&#x27;s so cool is that 99% of the tests you write with these | |
tools will look basically the same regardless of what framework you use! That&#x27;s | |
a huge win.</p><p>The hardest part is figuring out how to get some DOM from your component into | |
the document. And one of the course modules shows you how to do that with 11 | |
frameworks and libraries! I think you&#x27;ll really like this part of the course!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/react-is-an-implementation-detail">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Eliminate an entire category of bugs with a few simple tools]]></title> | |
<description><![CDATA[You've probably heard of ESLint, Prettier, and Flow/TypeScript. These are static | |
code analysis tools that are wildly popular in the JavaScript ecosystem. I | |
consider them all testing tools. Let's take a look at each: ESLint ESLint is the pluggable…]]></description> | |
<link>https://kentcdodds.com/blog/eliminate-an-entire-category-of-bugs-with-a-few-simple-tools</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/eliminate-an-entire-category-of-bugs-with-a-few-simple-tools</guid> | |
<pubDate>Thu, 18 Oct 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>You&#x27;ve probably heard of ESLint, Prettier, and Flow/TypeScript. These are static | |
code analysis tools that are wildly popular in the JavaScript ecosystem. I | |
consider them all testing tools. Let&#x27;s take a look at each:</p><h3><a href="https://eslint.org">ESLint</a></h3><p>ESLint is the pluggable linting utility for JavaScript. Linting is the process | |
of analyzing code for potential errors without actually running the code. | |
Consider this code:</p><pre><code class="language-js">if (!&#x27;serviceWorker&#x27; in navigator) { | |
// the user&#x27;s using an old browser :-( | |
} | |
</code></pre><p>Do you spot the problem? If you do, that&#x27;s great! But don&#x27;t you think it&#x27;d be | |
cool to not have to use your brain power to find and correct subtle bugs like | |
this one? I do! Make a computer do as much of my work for me as possible, please | |
and thank you. That&#x27;s what ESLint does for you.</p><h3><a href="https://prettier.io">Prettier</a></h3><p>Prettier is the JavaScript code formatter. It&#x27;ll take your code however you | |
write it, and reformat it in a way that&#x27;s consistent and legible every time. | |
People often give me quizzical looks when I refer to Prettier as a testing tool. | |
But check this out:</p><pre><code class="language-js">const a = false | |
const b = false | |
const c = true | |
const d = a &amp;&amp; b || c | |
</code></pre><p>What&#x27;s the value of <code>d</code> here? Do you know the order of operations of those | |
operators by heart? If you do, great! But do you trust that all the engineers on | |
your team know them well enough to not introduce a bug when refactoring this?</p><p>Run that code through Prettier, and this is what you get:</p><pre><code class="language-js">const a = false | |
const b = false | |
const c = true | |
const d = (a &amp;&amp; b) || c | |
</code></pre><p>Even if you do know the order of operations, the extra parentheses — which | |
Prettier automatically adds when you save the file — are quite helpful. And if | |
you realize that&#x27;s not what you wanted, then you can add the parentheses | |
yourself and Prettier will leave it that way (<code>const d = a &amp;&amp; (b || c)</code>).</p><p>This is one example of things Prettier does to make the intent of your code more | |
obvious — freeing your brain to focus on harder problems.</p><h3><a href="https://flow.org">Flow</a>/<a href="https://www.typescriptlang.org">TypeScript</a></h3><p>These are static type checkers for JavaScript. A static type checker adds syntax | |
to JavaScript to allow you to specify what data type a variable is. It can | |
follow that variable through the code to make sure it&#x27;s being used properly. (No | |
more <code>x is not a function</code>.)</p><p>Can you spot the bug in this code?</p><pre><code class="language-js">function getFullName(user) { | |
const { | |
name: {first, middle, last}, | |
} = user | |
return [first, middle, last].filter(Boolean).join(&#x27;&#x27;) | |
} | |
getFullName({first: &#x27;Joe&#x27;, middle: &#x27;Bud&#x27;, last: &#x27;Matthews&#x27;}) | |
</code></pre><p>Maybe you can, maybe you can&#x27;t. Maybe your coworkers can, maybe they can&#x27;t. In | |
any case, wouldn&#x27;t it be cool if we had some software that could spot the issue | |
for us? If we run that code with Flow, here&#x27;s what we get:</p><pre><code>Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ flow-example.js:8:13 | |
Cannot call getFullName with object literal bound to user because | |
property name is missing in object literal [1]. | |
5│ return [first, middle, last].filter(Boolean).join(&#x27;&#x27;) | |
6│ } | |
7│ | |
[1] 8│ getFullName({first: &#x27;Joe&#x27;, middle: &#x27;Bud&#x27;, last: &#x27;Matthews&#x27;}) | |
9│ | |
10│ | |
</code></pre><p>So without changing our code at all, we get notified something&#x27;s wrong. Nice! | |
Now, what if we add type annotations?</p><pre><code class="language-js">// @flow | |
type User = { | |
name: { | |
first: string, | |
middle: string, | |
last: string, | |
}, | |
} | |
function getFullName(user: User): string { | |
const { | |
name: {first, middle, last}, | |
} = user | |
return [first, middle, last].filter(Boolean).join(&#x27;&#x27;) | |
} | |
getFullName({first: &#x27;Joe&#x27;, middle: &#x27;Bud&#x27;, last: &#x27;Matthews&#x27;}) | |
</code></pre><p>Now if we run Flow on this, the error is even more helpful:</p><pre><code>Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ flow-example.js:15:13 | |
Cannot call getFullName with object literal bound to user because | |
property name is missing in object literal [1] but exists in | |
User [2]. | |
[2] 10│ function getFullName(user: User): string { | |
11│ const {name: {first, middle, last}} = user | |
12│ return [first, middle, last].filter(Boolean).join(&#x27;&#x27;) | |
13│ } | |
14│ | |
[1] 15│ getFullName({first: &#x27;Joe&#x27;, middle: &#x27;Bud&#x27;, last: &#x27;Matthews&#x27;}) | |
16│ | |
17| | |
</code></pre><p>I like to consider type definitions with Flow or TypeScript to be a form of | |
inline automated tests. I strongly recommend you give it a shot if you haven&#x27;t | |
yet. Incremental adoption is possible with these tools (especially if you&#x27;re | |
already using babel, you can just start using <code>babel-preset-{flow,typescript}</code>). | |
Try it out on your next feature and see what you think.</p><h3>Conclusion</h3><p>Static code analysis is a great way to get a significant boost of | |
confidence — fast, easily, and with less effort than writing unit tests for the | |
entire codebase. That&#x27;s why it forms the base of | |
<a href="https://twitter.com/kentcdodds/status/960723172591992832">the Testing Trophy 🏆</a>. | |
If you&#x27;re not using these tools already, start now.</p><p>Oh, and that big thing I&#x27;m working on that I&#x27;ve been teasing you about? I&#x27;ve got | |
a bunch of stuff in there showing how to set up these tools. Look forward to it | |
😉</p><p>Subscribe <a href="http://kcd.im/news">here</a>:</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://youtu.be/7LmrS2sdMlo?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">React... Suspense... (@ SLC frontend meetup)</a> — Livestream | |
of my talk at | |
<a href="https://www.meetup.com/SLC-FE-DEV/events/254256621">https://www.meetup.com/SLC-FE-DEV/eve...</a>. | |
In this talk I build my own simple-cache-provider to teach you how React | |
Suspense works with the cache and resources (promise throwers).</li><li><a href="https://github.com/facebook/create-react-app/issues/5103">Last call for Create React App v2</a> + | |
<a href="https://youtu.be/1ERAJG9ILhk?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">Using and writing custom babel macros with create-react-app v2</a> | |
(which is a <a href="http://kcd.im/devtips">DevTipsWithKent livestream</a>)</li><li><a href="https://youtu.be/kN7-EBjSilU?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">Validation with HTMLInputElements</a> — A | |
<a href="http://kcd.im/devtips">DevTipsWithKent</a> livestream where I show you a sweet | |
validation feature that&#x27;s built-into browsers since IE10!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/eliminate-an-entire-category-of-bugs-with-a-few-simple-tools">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Why you've been bad about testing]]></title> | |
<description><![CDATA[The argument is long ended: You should be testing your mission-critical | |
code. Everyone accepts that testing code now is better than waiting for users to | |
complain later . Everyone agrees that the testing should be automated. Pretty much everyone's…]]></description> | |
<link>https://kentcdodds.com/blog/why-youve-been-bad-about-testing</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/why-youve-been-bad-about-testing</guid> | |
<pubDate>Mon, 15 Oct 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>The argument is long ended: <strong>You should be testing your mission-critical | |
code.</strong></p><p>Everyone accepts that testing <em>code</em> now is better than waiting for users to | |
complain <em>later</em>.</p><p>Everyone agrees that the testing should be automated.</p><p>Pretty much everyone&#x27;s had a situation where tests saved them from a production | |
bug...or would have saved them if tests had been in place.</p><p>But there&#x27;s still a struggle. | |
<a href="https://twitter.com/kentcdodds/status/1048645068616163328">The other day I asked on twitter</a> | |
what you all struggle with around testing. The struggle comes when you&#x27;re trying | |
to determine what to test, how much time you have to test, what granularity to | |
test, what to mock and what to keep real, or just the daunting task of setting | |
up good testing tools and testing environments. Or the frustration might creep | |
in when you find yourself writing the same code in two places. 🤪</p><p>Another big source of frustration with testing — stop us if you&#x27;ve heard this | |
one before — is when you have to basically rewrite your tests every time you | |
touch the code it&#x27;s testing. Once you pick your head up off your keyboard, you | |
have to ask yourself: &quot;Ok... so why are we testing again? This is just adding | |
friction to shipping code.&quot; It&#x27;s extremely frustrating when you refactor a | |
component and your test breaks. 😡 I wouldn&#x27;t want to write tests either.</p><p>When I got started with testing years ago, I struggled. I struggled hard. I&#x27;ve | |
spent countless hours learning, building, and rebuilding tools. I even gave a | |
<em>full hour</em> talk called | |
<a href="https://kentcdodds.com/talks/#es6-webpack-karma-and-code-coverage">&quot;ES6, Webpack, Karma, and Code Coverage&quot;</a>. | |
It took a full 60 minutes to explain how to make these tools play nicely | |
together. It took dozens more hours behind the scenes to figure out what I | |
explain in that talk.</p><p>But I was committed to figuring out and helping build the best way to do this. I | |
needed it, and I know my fellow developers need it too.</p><p>Struggle and frustration don&#x27;t have to be your experience. You don&#x27;t have to | |
spend dozens of hours to figure out how to get testing set up in your codebase. | |
<strong>I&#x27;ve already done that for you.</strong> And the tools have improved, and I can show | |
you how to use them. There are techniques I can teach you that will alleviate | |
the pain and struggle you&#x27;re having with getting the confidence you&#x27;re looking | |
for out of your testbase.</p><p><strong>Here&#x27;s a tip</strong> for you to take the next time you test your code that&#x27;ll help | |
you answer the question of &quot;what do I test?&quot; Follow this process:</p><ol><li>What part of your untested codebase would be really bad if it broke? (The | |
checkout process)</li><li>Try to narrow it down to a unit or a few units of code (When clicking the | |
&quot;checkout&quot; button a request with the cart items is sent to /checkout)</li><li>Look at that code and consider who the &quot;users&quot; are (The developer rendering | |
the checkout form, the end user clicking on the button)</li><li>Write down a list of instructions for that user to manually test that code | |
to make sure it&#x27;s not broken. (render the form with some fake data in the | |
cart, click the checkout button, ensure the mocked /checkout API was called | |
with the right data, respond with a fake successful response, make sure the | |
success message is displayed).</li><li>Turn that list of instructions into an automated test.</li></ol><p>With the tools and techniques I&#x27;m going to show you, this process will become a | |
natural habit. 💯</p><p>Stop the struggle. Follow me.</p><p>P.S. Stay tuned, because over the next couple weeks I&#x27;ll share more strategy and | |
tactics for getting your testing skills up to date.</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://testingjavascript.com">TestingJavaScript.com</a> — This is the huge | |
thing that I wont stop talking about 😉</li><li><a href="https://github.com/palmerhq/react-suspense-starter"><code>react-suspense-starter</code></a>: | |
If you haven&#x27;t had a chance to checkout suspense yet and play around with it, | |
check this out :)</li><li><a href="http://kcd.im/devtips">DevTipsWithKent</a> — If you haven&#x27;t watched any of these | |
yet, I recommend you give them a look. I&#x27;m going to be doing some scheduled | |
livestreams soon that you&#x27;ll want to catch live for sure!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/why-youve-been-bad-about-testing">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Demystifying Testing]]></title> | |
<description><![CDATA[In the next few weeks, you're going to get bonus emails from me as I prepare to | |
launch the biggest undertaking I've ever taken. You'll love it. (Trust me, I've | |
run the tests. 😉) Many of you have messaged me, confused about where to get started with…]]></description> | |
<link>https://kentcdodds.com/blog/demystifying-testing</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/demystifying-testing</guid> | |
<pubDate>Thu, 11 Oct 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>In the next few weeks, you&#x27;re going to get bonus emails from me as I prepare to | |
launch the biggest undertaking I&#x27;ve ever taken. You&#x27;ll love it. (Trust me, I&#x27;ve | |
run the tests. 😉)</p><p>Many of you have messaged me, confused about where to get started with testing. | |
Just like everything else in software, we work hard to build abstractions to | |
make our jobs easier. But that amount of abstraction evolves over time, until | |
the only ones who <em>really</em> understand it are the ones who built the abstraction | |
in the first place. Everyone else is left with taking the terms, APIs, and tools | |
at face value and struggling to make things work.</p><p>If there&#x27;s one thing I believe about abstraction in code, it&#x27;s that the | |
abstraction is <em>not</em> magic, it&#x27;s code. If there&#x27;s another I thing I believe | |
about abstraction in code, it&#x27;s that it&#x27;s easier to learn by doing.</p><p>Here&#x27;s an example:</p><p>Imagine that a less seasoned engineer approaches you. They&#x27;re hungry to learn, | |
they want to be confident in their code, and they&#x27;re ready to start testing. 👍 | |
Ever prepared to learn from you, they&#x27;ve written down a list of terms, APIs, and | |
concepts they&#x27;d like you to define for them:</p><ul><li>Assertion</li><li>Testing Framework</li><li>The <code>describe</code>/<code>it</code>/<code>beforeEach</code>/<code>afterEach</code>/<code>test</code> functions</li><li>Mocks/Stubs/Test Doubles/Spies</li><li>Unit/Integration/End to end/Functional/Accessibility/Acceptance/Manual testing</li></ul><p>So.........</p><p>Could you rattle off definitions for that budding engineer? Can you explain the | |
difference between an assertion library and a testing framework? Or are they | |
easier for you to <em>identify</em> than <em>explain</em>?</p><p>Here&#x27;s the point. The better you understand these terms and abstractions, the | |
more effective you will be at teaching them. And if you can teach them, <strong>you&#x27;ll | |
be more effective at <em>using</em> them, too.</strong></p><p>Enter a teach-an-engineer-to-fish moment. Did you know that you can <em>write your | |
own</em> assertion library and testing framework? We often think of these | |
abstractions as beyond our capabilities, but they&#x27;re not. Each of the popular | |
assertion libraries and frameworks started with a single line of code, followed | |
by another and then another. <strong>You don&#x27;t need any tools to write a simple | |
test.</strong> Here&#x27;s an example:</p><pre><code class="language-js">const {sum} = require(&#x27;../math&#x27;) | |
const result = sum(3, 7) | |
const expected = 10 | |
if (result !== expected) { | |
throw new Error(`${result} is not equal to ${expected}`) | |
} | |
</code></pre><p>Put that in a module called <code>test.js</code> and run it with <code>node test.js</code> and poof, | |
you can start getting confidence that the <code>sum</code>function from the <code>math.js</code> | |
module is working as expected. Make that run on CI and you can get the | |
confidence that it won&#x27;t break as changes are made to the codebase. 🏆</p><p>Once you understand how the abstractions work at a fundamental level, you&#x27;ll | |
probably want to use them because hey, you just learned to fish and now you can | |
go fishing. And we have some pretty phenomenal fish, uh, tools available to us. | |
My favorite is the <a href="https://jestjs.io">Jest</a> testing platform. It&#x27;s amazingly | |
capable and fully featured and allows me to write tests that give me the | |
confidence I need to not break things as I change code.</p><p>I&#x27;m really looking forward to what I&#x27;m creating for you. I think it&#x27;ll help | |
accelerate your understanding of testing tools and abstractions by giving you | |
the chance to implement parts from scratch. The (hopeful) result? You can start | |
writing tests that are maintainable and built to instill confidence in your code | |
day after day.</p><p>Stay tuned. 🎣</p><p>P.S. Give this a try: | |
<a href="https://twitter.com/intent/tweet?status=%23AssertionLibVsTestingFramework">Tweet</a> | |
what&#x27;s the difference between a testing framework and an assertion library? In | |
my course, I&#x27;ll not only explain it, we&#x27;ll build our own!</p><p>P.P.S. Set your alarms and | |
<a href="https://twitter.com/intent/tweet?status=%E2%9A%A0%EF%B8%8F%20Hey%20friends!%20%40kentcdodds%20is%20working%20on%20a%20HUGE%20series%20of%20courses%20%28%3E100%20videos%20total!%29%20and%20you%20don%27t%20want%20to%20miss%20it.%20Subscribe%20to%20his%20newsletter%20to%20get%20a%20special%20discount%20when%20it%27s%20launched%3A%20kcd.im/news%20%F0%9F%92%8C">tell your friends</a>! | |
On <strong>Friday Oct 19th</strong> my course goes on sale with early-bird pricing! It is | |
HUGE. Like over 100 dense videos huge. Like ~5 dense hours of testing huge. | |
Seriously, <strong>let people know.</strong> They don&#x27;t want to miss this.</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/demystifying-testing">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Confidently Shipping Code]]></title> | |
<description><![CDATA[Have you read the book "Start With Why" ? If | |
you haven't, I recommend it. At least watch | |
the TED talk . The premise of the idea is that | |
"People won't truly buy into a product, service, movement, or idea until they | |
understand the WHY behind it…]]></description> | |
<link>https://kentcdodds.com/blog/confidently-shipping-code</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/confidently-shipping-code</guid> | |
<pubDate>Mon, 08 Oct 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Have you read the book <a href="https://startwithwhy.com/books">&quot;Start With Why&quot;</a>? If | |
you haven&#x27;t, I recommend it. At least watch | |
<a href="https://youtu.be/u4ZoJKF_VuA">the TED talk</a>. The premise of the idea is that | |
&quot;People won&#x27;t truly buy into a product, service, movement, or idea until they | |
understand the WHY behind it.&quot; This concept hit home with me when I watched that | |
talk and then read the book a few years ago, and it&#x27;s shaped the way I | |
communicate with you in my blog posts, talks, workshops, and courses.</p><p>I&#x27;d like to share my &quot;why&quot; about testing with you and invite you to consider | |
what your own &quot;why&quot; might be.</p><p>Like many people, at the start of my career I didn&#x27;t understand automated | |
software testing. I was introduced to it by my friend | |
<a href="https://twitter.com/josepheames">Joe Eames</a>, who was then fighting an uphill | |
battle at the company where we both worked to get people to write tests. I was | |
an intern at the time, and he took me under his wing a few times to teach me | |
about automated testing. I thought it was pretty neat, but he moved on to | |
another job before I really got testing ingrained in my workflow.</p><p>Later, when I started writing my first JavaScript library | |
(<a href="https://github.com/kentcdodds/genie">geniejs</a>), I realized quickly that | |
spending time to manually verify that everything&#x27;s working, every single time I | |
fixed a bug or added a new feature, was pretty annoying. I decided to learn to | |
test and write tests for my library. | |
(<a href="https://github.com/kentcdodds/genie/blob/166572f9fa82e6ec0893f90c7d6a99a49632dace/src/__tests__/index.js">The tests</a> | |
have been through a few testing framework refactorings, but they&#x27;re still | |
largely the same as when I originally wrote them all those years ago). Investing | |
time into testing my library ended up saving me a TON of time, and I was able to | |
integrate testing into my workflow.</p><p>I remember when I was actively working on angular-formly, I had a coworker who | |
needed a new feature. It was a simple feature, so he sat by me and watched as I | |
wrote the test, implemented the feature, and pushed the commit to trigger a | |
release. He was shocked that I could rely on the tests so much that I was | |
confident I didn&#x27;t break anything. That was when it really occurred to me that | |
testing had become more than a default workflow for saving time. It was a | |
mechanism for giving me confidence.</p><p>At the time of this writing, | |
<a href="https://www.npmjs.com/~kentcdodds">I have 111 packages published on npm</a>. | |
Pretty much every one of those packages has 100% code coverage, meaning every | |
line is run in the tests. I don&#x27;t think I could possibly maintain them any other | |
way. <strong>My libraries have received contributions from thousands of people.</strong> When | |
someone opens a pull request with changes to one of my libraries, I have a | |
continuous integration service (<a href="https://travis-ci.org">TravisCI</a>) that kicks | |
off to run all the tests. Sometimes it&#x27;s been months or even years since I&#x27;ve | |
touched the code. Past Kent, who had just barely written the code, probably knew | |
instantly whether something broke. Present Kent? He usually has no idea, and it | |
would take me a lot of time to evaluate. Having the tests in place is like Past | |
Kent telling Present Kent: &quot;It&#x27;s ok. This is very unlikely to break anything.&quot; | |
The tests save me time, and they give me — and all the users of my libraries — a | |
great amount of peace of mind.</p><p><strong>So why do I write tests?</strong> I write tests because they allow me to accomplish | |
more than I could otherwise. I now have thousands of Kents in the form of | |
automated tests telling me whether changes are breaking use cases. With that | |
venerable army of robots, I&#x27;m able to rest easy and get more accomplished.</p><p><strong>Now, I want to ask you a question:</strong> Why do you want to learn testing? Is it | |
to further your career? Did a specific incident (i.e. a bug — yup, we&#x27;ve all | |
been there) happen that prompted a need? Do you (like me) simply want to get | |
more done, with more peace of mind?</p><p><strong>Now my next question:</strong> What has been holding you back from starting?</p><h3>Conclusion</h3><p>Whether you&#x27;re already testing, or you&#x27;re interested in getting started, I&#x27;ve | |
got something coming that I think you&#x27;ll love. Especially if you&#x27;ve struggled to | |
know what to test. I&#x27;ve been working hard putting together <strong>the most | |
comprehensive work of my life</strong>, and I think it&#x27;ll knock your socks off. Stay | |
tuned.</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://testingjavascript.com">TestingJavaScript.com </a>— I&#x27;m about to release | |
a HUGE series of courses that you&#x27;ll definitely want to checkout. Early bird | |
pricing starts on October 19th.</li><li><a href="https://reach.tech/workshops?a=kent">Professional React Training from Ryan Florence</a> — I | |
strongly recommend you look. <a href="https://twitter.com/ryanflorence">Ryan&#x27;s</a> doing | |
a tour to 12 cities in the US!!</li><li><a href="http://kcd.im/devtips">DevTips with Kent</a> — I&#x27;m still doing (week)daily | |
livestreams that hundreds of people watch :) Don&#x27;t miss it!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/confidently-shipping-code">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[React/JSX as a server-side templating language]]></title> | |
<description><![CDATA[Another note: | |
I've been teasing | |
about something big that I have coming. I'm totally not joking. I'm working on | |
something really huge and y'all will be the first to know about it. Stay | |
tuned. It's weeks away and I think you're going to love it. Last…]]></description> | |
<link>https://kentcdodds.com/blog/react-jsx-as-a-server-side-templating-language</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/react-jsx-as-a-server-side-templating-language</guid> | |
<pubDate>Mon, 01 Oct 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<blockquote><p><em>Another note: | |
<a href="https://twitter.com/kentcdodds/status/1041712678970875904">I&#x27;ve been teasing</a> | |
about something big that I have coming. I&#x27;m totally not joking. I&#x27;m working on | |
something really huge and y&#x27;all will be the first to know about it. Stay | |
tuned. It&#x27;s weeks away and I think you&#x27;re going to love it.</em></p></blockquote><p>Last week at PayPal, one of my pull requests was merged in an express codebase | |
which migrated us from a custom template system to using React function | |
components and JSX. The motivation was to reduce the maintenance overhead of | |
knowing and maintaining a custom template system in addition to the JSX we are | |
doing on the frontend.</p><p>The app is <a href="https://paypal.me">paypal.me</a>. The way it works is we have the | |
<a href="https://www.paypal.me">home</a>, | |
<a href="https://www.paypal.com/paypalme/pages/terms">terms</a>, and | |
<a href="https://www.paypal.me/pages/countries">supported countries</a> pages that are 100% | |
rendered HTML/CSS (and just a tiny bit of vanilla JS), and then the | |
<a href="https://www.paypal.me/kentcdodds/10">profile</a> and | |
<a href="https://www.paypal.com/paypalme/my/profile">settings</a> pages are rendered by the | |
server as &quot;skeleton&quot; html pages (with SEO-relevant tags and a root <code>&lt;div&gt;</code> etc.) | |
and then the client-side React app kicks in to load the rest of the | |
data/interactivity needed on the page.</p><blockquote><p><em>I should note that generally I&#x27;d suggest that if you&#x27;re doing any server | |
rendering at all, you&#x27;d probably find better performance doing server | |
rendering for everything (using something like <a href="https://nextjs.org">Next.js</a> | |
or <a href="https://www.gatsbyjs.org">gatsby</a> if you can), not just the skeleton | |
<code>index.html</code> as we&#x27;re doing on <a href="http://paypal.me">paypal.me</a>. We have our | |
reasons (there&#x27;s nuance in everything and I&#x27;m not going to get into this).</em></p></blockquote><p>Before my PR, we actually had two systems in place. We used | |
<a href="https://github.com/dondido/express-es6-template-engine"><code>express-es6-template-engine</code></a> | |
for the profile and settings pages (which are actually the same page), and for | |
the marketing pages one of our engineers came up with a tagged-template literal | |
solution that was react-like (with functions that accepted props and returned a | |
string of HTML). So engineers that work on this codebase would have to know and | |
maintain:</p><ol><li><code>express-es6-template-engine</code> for the profile and settings pages</li><li>React and JSX for the client-side app</li><li>The custom tagged-template literal solution for the marketing pages.</li></ol><p>It was decided to simplify this down to a single solution: React and JSX for | |
both frontend and backend. And that&#x27;s the task I took. I want to explain a few | |
of the gotchas and solutions that I ran into while making this transition.</p><h3>JSX compilation</h3><p>This was actually as easy as <code>npm install --save react react-dom</code> in the | |
<code>server</code>. Because <a href="http://paypal.me">paypal.me</a> uses | |
<a href="https://kentcdodds.com/blog/tools-without-config">paypal-scripts</a>, the server&#x27;s already compiled with | |
the built-in babel configuration which will automatically add the necessary | |
react plugins if the project lists react as a dep. Nice! I LOVE Toolkits!</p><h3>HTML Structure</h3><p>The biggest challenge I faced with this involves integration with other PayPal | |
modules that generate HTML that need to be inserted into the HTML that we&#x27;re | |
rendering. One such example of this is our polyfill service that | |
<a href="https://kentcdodds.com/blog/polyfill-as-needed-with-polyfill-service">I wrote about a while back</a>which | |
inserts a script tag that has some special query params and | |
<a href="https://en.wikipedia.org/wiki/Cryptographic_nonce">a server nonce</a>. We have | |
this as middleware and it adds a <code>res.locals.polyfill.headHTML</code> which is a | |
string of HTML that needs to appear in the <code>&lt;head&gt;</code> that you render.</p><p>With the template literal and es6-template-engine thing we had, this was pretty | |
simple. Just add <code>${polyfill.headHTML}</code> in the right place and you&#x27;re set. In | |
React though, that&#x27;s kinda tricky. Let&#x27;s try it out. Let&#x27;s assume that | |
<code>polyfill.headHTML</code> is <code>&lt;script src=&quot;hello.js&quot;&gt;&lt;/script&gt;</code>. So if we do this:</p><pre><code class="language-jsx">&lt;head&gt;{polyfill.headHTML}&lt;/head&gt; | |
</code></pre><p>This will result in HTML that looks like this:</p><pre><code class="language-jsx">&lt;head&gt;&amp;lt;script src=&amp;quot;hello.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/head&gt; | |
</code></pre><p>This is because React escapes rendered interpolated values (those which appear | |
between <code>{</code> and <code>}</code>). This is a | |
<a href="https://en.wikipedia.org/wiki/Cross-site_scripting">cross site-scripting (XSS)</a> | |
protection feature built-into React. All of our apps are safer because React | |
does this. However, there are situations where it causes problems (like this | |
one). So React gives you an escape hatch where you can opt-out of this | |
protection. Let&#x27;s use that:</p><pre><code class="language-jsx">&lt;head&gt; | |
&lt;div dangerouslySetInnerHTML={{__html: polyfill.headHTML}} /&gt; | |
&lt;/head&gt; | |
</code></pre><p>So this would result in:</p><pre><code class="language-jsx">&lt;head&gt; | |
&lt;div&gt; | |
&lt;script src=&quot;hello.js&quot; /&gt; | |
&lt;/div&gt; | |
&lt;/head&gt; | |
</code></pre><p>But that&#x27;s not at all semantically accurate. A <code>div</code> should not appear in a | |
<code>head</code>. We also have some <code>meta</code> tags. It technically works in Chrome, but I | |
don&#x27;t know what would happen in all the browsers PayPal supports and I don&#x27;t | |
want to bust SEO or functionality of older, less-forgiving browsers for this.</p><p>So here&#x27;s the solution I came up with that I don&#x27;t hate:</p><pre><code class="language-jsx">&lt;head&gt; | |
&lt;RawText&gt;{polyfill.headHTML}&lt;/RawText&gt; | |
&lt;/head&gt; | |
</code></pre><p>The implementation of that <code>RawText</code> component is pretty simple:</p><pre><code class="language-jsx">function RawText({children}) { | |
return &lt;raw-text dangerouslySetInnerHTML={{__html: children}} /&gt; | |
} | |
</code></pre><p>So this will result in:</p><pre><code class="language-html">&lt;head&gt; | |
&lt;raw-text&gt; | |
&lt;script src=&quot;hello.js&quot; /&gt; | |
&lt;/raw-text&gt; | |
&lt;/head&gt; | |
</code></pre><p>This doesn&#x27;t solve the problem by itself. Here&#x27;s what we do when we render the | |
page to HTML:</p><pre><code class="language-jsx">const htmlOutput = ReactDOMServer.renderToStaticMarkup(&lt;Page {...options} /&gt;) | |
const rendered = ` | |
&lt;!DOCTYPE html&gt; | |
${removeRawText(htmlOutput)} | |
` | |
// ...etc... | |
</code></pre><p>That <code>removeRawText</code> function is defined right next to the <code>RawText</code> component | |
and looks like this:</p><pre><code class="language-js">function removeRawText(string) { | |
return string.replace(/&lt;\/?raw-text&gt;/g, &#x27;&#x27;) | |
} | |
</code></pre><p>So, effectively what our <code>rendered</code> string looks like is this:</p><pre><code class="language-html">&lt;head&gt; | |
&lt;script src=&quot;hello.js&quot; /&gt; | |
&lt;/head&gt; | |
</code></pre><p>🎉 Cool right?</p><p>So we have a simple component we can use for any raw string we want inserted | |
as-is into the document without having to add an extra meaningless (and | |
sometimes semantically harmful) DOM node in the mix. (Note, the real solution to | |
this problem would be for React to | |
<a href="https://github.com/facebook/react/issues/12014">support</a> | |
<a href="https://github.com/facebook/react/issues/12014"><code>dangerouslySetInnerHTML</code></a> | |
<a href="https://github.com/facebook/react/issues/12014">on Fragments</a>).</p><blockquote><p><em><strong>NOTE:</strong> The fact that this logic lives in a function right next to the | |
definition of the <code>RawText</code> component rather than just hard-coding the | |
replacement where it happens is IMPORTANT. Anyone coming to the codebase and | |
seeing <code>RawText</code> or <code>removeRawText</code> will be able to find out what&#x27;s going on | |
much more quickly.</em></p></blockquote><h3>Localization</h3><p>In our client-side app, we use a localization module that my friend Jamund and I | |
worked on that relies on a singleton &quot;store&quot; of content strings. It works great | |
because there&#x27;s only one locale that&#x27;ll ever be needed through the lifetime of | |
the client-side application. Singletons don&#x27;t work very well on the backend | |
though. So I built a simple React Context consumer and provider which made it | |
easier to get messages using this same abstraction without the singleton. I&#x27;m | |
not going to share the code for it, but here&#x27;s how you can use it:</p><pre><code class="language-jsx">&lt;Message msgKey=&quot;marketing_pages/new_landing.title&quot; /&gt; | |
</code></pre><p>It worked out pretty well. The <code>Message</code> component renders the <code>MessageConsumer</code> | |
component which will get the content out of context and retrieve the message | |
with the given key.</p><h3>Other things of note:</h3><ul><li><a href="https://reactjs.org/docs/fragments.html"><code>React.Fragments</code></a> are everywhere. | |
When the structure matters so much, you find yourself using React fragments | |
all over the place. We&#x27;re using babel 7 and loving the new shorter syntax of | |
<code>&lt;&gt;</code> and <code>&lt;/&gt;</code>.</li><li><code>style</code>/<code>className</code> changes. Before this was straightup HTML, the biggest | |
changes I had to make was all the <code>class=&quot;</code>had to be changed to <code>className=&quot;</code> | |
which wasn&#x27;t all that challenging, but I found myself forgetting the | |
<code>style=&quot;</code>attributes needing to be changed to <code>style={</code> and object syntax all | |
the time. Luckily React gives you a warning if you miss one :)</li><li><code>${</code> needed to be changed to <code>{</code>. I found a few stray <code>$</code> rendered several | |
times in the course of this refactor 😅</li></ul><h3>Conclusion</h3><p>I&#x27;m pretty pleased that we now only have one templating solution for the entire | |
app (both frontend and backend). I think that&#x27;ll reduce the maintenance burden | |
of the app and that&#x27;s a real win. Trying things out and doing experiments is a | |
good thing, but circling back to refactor things to the winning abstraction is | |
an important step to making applications that are maintainable for the | |
long-term. I hope this is helpful to you! Good luck!</p><p><strong>Learn more about React from me</strong>:</p><ul><li><a href="http://kcd.im/beginner-react">The Beginner&#x27;s Guide to React</a></li><li><a href="http://kcd.im/advanced-react">Advanced React Component Patterns</a> (also on | |
<a href="https://frontendmasters.com/courses/advanced-react-patterns">Frontend Masters</a>).</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://youtu.be/SAIdyBFHfVU?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">The introduction to React you&#x27;ve been missing</a> — My | |
talk from UtahJS Conf 2018. Lots of livecoding here. In this talk I teach | |
React from scratch in a single index.html file with no magic up my sleeves. We | |
start with a basic Hello World in vanilla JavaScript and incrementally iterate | |
through React APIs and JSX. We continue with introducing more of React&#x27;s APIs. | |
<a href="https://youtube.com/playlist?list=PLuVqdWOQ-PNn_lDYUVgcA4e91qxJzipva">Watch all the talks from UtahJS Conf 2018</a></li><li><a href="https://youtube.com/playlist?list=PLV5CVI1eNcJhU1eyqkTjR0B5P7PzMVubB">Testing React Components @ PayPal 2018–09</a> — I | |
gave a ~4 hour workshop at PayPal last week and livestreamed it. | |
<a href="https://github.com/kentcdodds/react-testing-library-course/tree/workshop-2018-09">Here&#x27;s the material</a>.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/react-jsx-as-a-server-side-templating-language">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How I am so productive]]></title> | |
<description><![CDATA[I get asked about this at least twice a week, so I thought I'd save myself some | |
time by writing a blog post I can reference instead of answering the same | |
question repeatedly (spoiler, this is one of my secrets). To help give you context, here are…]]></description> | |
<link>https://kentcdodds.com/blog/how-i-am-so-productive</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-i-am-so-productive</guid> | |
<pubDate>Mon, 24 Sep 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I get asked about this at least twice a week, so I thought I&#x27;d save myself some | |
time by writing a blog post I can reference instead of answering the same | |
question repeatedly (spoiler, this is one of my secrets).</p><p>To help give you context, here are some of the things I do on a fairly regular | |
basis these days:</p><ul><li>I teach Sunday school to ~6 nine-year-old kids (we&#x27;re going through the | |
<a href="https://www.mormon.org/free-bible">Old Testament</a> this year). In addition to | |
attending 3 hours of church, I also prepare and deliver a one hour lesson for | |
them every other week.</li><li>On Sundays I generally don&#x27;t commit any code or do any &quot;work.&quot; I have church | |
and family time and responsibilities (though I do enjoy spending the evening | |
with | |
<a href="https://twitter.com/craigwalker1123/status/1039138835714560000">new friends playing Dominion</a>)</li><li>On Mondays, I publish the blogpost newsletter from two weeks ago to | |
<a href="http://kentcdodds.com/blog">kentcdodds.com/blog</a>, a new one for subscribers. | |
(~1.5 hours of work, depending...)</li><li>Product tasks (currently helping work on <a href="https://paypal.me">paypal.me</a>) (this | |
is normally where most of my weekday is spent)</li><li>Attend work meetings</li><li>Help people on slack/video chat</li><li>Work on <a href="https://kentcdodds.com/blog/tools-without-config">paypal-scripts</a> (among other internal | |
libraries and tools)</li><li>traveling/training (about once a quarter. I&#x27;m on an airplane right now for | |
such an engagement)</li><li>Research, prepare, and do a | |
<a href="http://kcd.im/devtips">DevTips with Kent livestream</a> (~20 minutes... | |
sometimes) (week-daily)</li><li>Respond to literally dozens of GitHub issues/PRs (daily)</li><li>Code up or help people code up solutions to the GitHub issues</li><li>Release ~5 of any of my &gt;100 npm modules multiple times a day</li><li>Travel to (sometimes) and speak at a conference (about once or twice a month)</li><li>Travel to (sometimes) and deliver a 1–2 day workshop on one of five topics I | |
regularly train about.</li><li>Record <a href="http://egghead.io">egghead.io</a> lessons and courses (working on some of | |
this very hard core right now)</li><li>Research and prepare educational material (for | |
talks/workshops/courses/devtips/etc.)</li><li>Go out to lunch with a (new) friend in the community about once a week</li><li>Outline and prepare for writing my novel for | |
<a href="https://nanowrimo.org">NaNoWriMo</a> (which is going to be epic by the way).</li><li><a href="http://kcd.im/ama">AMA</a>: Ask Me Anything (currently has 475 questions I&#x27;ve | |
answered).</li><li><a href="http://kcd.im/tech-chats">TechChats</a>: livestream video chats with people | |
about tech (about once a month or so).</li><li>Publish a 3 minute podcast (on <a href="http://kcd.im/3-mins">3 minutes with Kent</a>)</li><li>Be a guest on podcasts (about once a month or so).</li><li>Respond to literally dozens of questions per day coming from Tweets (mostly), | |
Twitter DMs (several daily), Slack, email, YouTube comments, and pigeon (one | |
of those is a joke).</li></ul><p>On top of all that, I&#x27;m married, have 4 kids (6 and younger) and a puppy, and | |
have a home and yard/garden to care for.</p><h3>A typical day</h3><p>Let me share a fairly general weekday with you:</p><p>Normally I wake up at ~7:00 AM and I&#x27;m ready for work by ~9:00 AM. I sit at my | |
desk (I work from home) and start with my daily scripture study for a few | |
minutes. Then I power on the computer, and start with my email and twitter (the | |
stuff I hadn&#x27;t addressed while brushing my teeth etc. 😅). If I&#x27;m aware of any | |
pressing work, I&#x27;ll take care of that first. If it&#x27;s Monday, I&#x27;ll make sure my | |
blog post is published, then start working on this week&#x27;s blogpost newsletter. | |
Then I do the DevTips with Kent livestream.</p><p>By this time it&#x27;s normally ~10:00 AM (or later on Mondays/if there&#x27;s pressing | |
work I had to do first). It&#x27;s likely that I&#x27;ve already released one or two new | |
versions of my npm modules, answered a half dozen questions, and | |
responded/reviewed several GitHub issues/PRs. Now I start on whatever PayPal | |
product work I&#x27;m working on (as I mentioned, I&#x27;m helping with | |
<a href="https://paypal.me">paypal.me</a> right now). I meet with my co-workers and decide | |
the highest priority tasks and get to work on them.</p><p>At ~12:00 PM (often later if I&#x27;m really involved in something), I go up and have | |
lunch with my family. If he&#x27;s not already in bed, I read a book to one of my | |
boys and put him down for a nap (working from home is the best). My lunch break | |
is normally ~30 minutes.</p><p>I spend the afternoon working on more PayPal stuff, meetings, helping answer | |
questions from all the various channels (and sadly ignoring many of them as I | |
have work to do), and releasing more versions of various OSS libraries/tools.</p><p>I wrap up the day between 5:00 PM and 6:00 PM and head upstairs. It&#x27;s family | |
time. Often I&#x27;ll hang out with my wife after the kids are in bed. Sometimes | |
though, if I&#x27;m working on a big course for <a href="http://egghead.io">egghead.io</a> or | |
something, I&#x27;ll go back to my office and start working on that. Normally I&#x27;ll go | |
to bed before 11:00 PM.</p><p>Saturdays are mostly yard work and family time. I don&#x27;t <em>normally</em> do much | |
coding on Saturdays. Sundays are family and church time. I very rarely do any | |
work on Sundays (occasionally I&#x27;ll merge simple PRs from my phone or get a head | |
start on this newsletter).</p><p>If this schedule sounds set in stone or a solid routine, let me assure you it&#x27;s | |
not. What I&#x27;ve written is a pretty general schedule that wasn&#x27;t really planned | |
and is just what kinda happened. In any case, I hope it helps to frame the rest | |
of my advice in a way that&#x27;s relatable and helpful to you.</p><h3>It&#x27;s an illusion</h3><p>If you read carefully, you&#x27;ll notice that I do a bunch of my stuff when I&#x27;m on | |
the clock at PayPal. That&#x27;s because the stuff I do is good for PayPal and my | |
bosses have appreciated that I do it. Just last week I had multiple different | |
engineers within PayPal thank me for the daily | |
<a href="http://kcd.im/devtips">DevTips with Kent</a> livestreams and | |
<a href="http://kcd.im/news">these newsletters</a>. PayPal is happy that I&#x27;m sharing my | |
knowledge and so long as what I share is not proprietary/legally concerning/etc, | |
they&#x27;re happy to let me continue doing that. (You could say this newsletter is | |
sponsored by PayPal! Thanks PayPal!).</p><p>PayPal employees also use a bunch of the open source software that I maintain. | |
So when I&#x27;m doing work on my open source projects during work hours, 90% of the | |
time it&#x27;s because we have a problem within PayPal that needs solving and I&#x27;m | |
just doing my job to make PayPal engineers more effective. Some of my projects | |
are libraries that I created at PayPal and then open sourced while others are | |
projects I created outside of my time at PayPal and now PayPal engineers use. In | |
either case, working on those projects (and contributing to other projects of | |
which I&#x27;m not a maintainer), is all part of my job.</p><p>So when people ask me: &quot;HOW DO YOU DO ALL THIS STUFF <em>AND</em> HAVE A JOB AT | |
PAYPAL!?&quot; My answer is: &quot;well... a lot of this stuff <em>is</em> my job at PayPal.&quot;</p><p>This brings me to my next point:</p><h3>Increase the impact of your value</h3><p>We&#x27;re all constantly creating value in the world. A conversation with your | |
co-worker over lunch about why, what, and how to do a git rebase is creating | |
value. A meetup talk you&#x27;re delivering is creating value. etc. etc. etc. The | |
secret that I&#x27;ve found is taking the value that you&#x27;re already creating, and | |
increase its impact by preserving and presenting it to the world.</p><p>So turn that conversation into a blog post or have that conversation over Google | |
Hangouts on Air and have it upload to YouTube automatically (which is what my | |
<a href="http://kcd.im/tech-chats">tech chats</a> are). Make sure your meetup talk is | |
recorded (even if that means you&#x27;re just recording your screen, which I do all | |
the time). Instead of answering your co-worker&#x27;s slack question about arrow | |
functions on slack, type it out as a quick blog post on medium, a gist, or a 🔥 | |
FIRE 🔥 TWEET 🔥 and send them the link.</p><p>As long as your company is cool with you sharing non-proprietary knowledge with | |
the world, then take advantage of that (as a side note, I would have a very hard | |
time being successful at a company which does not value open knowledge sharing | |
like this. I know it&#x27;s a privilege to work at a company like PayPal. Sorry if | |
you&#x27;re not in an environment like PayPal in this way.)</p><p>In short <a href="https://twitter.com/swyx/status/1009174159690264579">learn in public</a> | |
(I love you <a href="https://twitter.com/swyx">Shawn!</a>). It&#x27;s likely that if you listed | |
out all the things you do in a week your list would be just as long if not | |
longer than mine. The thing that makes it appear that I am so productive is that | |
I make public as much of what I do as possible.</p><h3>Automation</h3><p>If you maintain an npm package, it may surprise you (or you may be skeptical of | |
the fact) that I manage to release multiple versions of multiple packages in a | |
typical day. Believe me though, I release almost every PR made on my open source | |
projects within minutes of my merging them into <code>master</code>, and often I do so from | |
my phone.</p><p>This is possible because my open source projects have a solid suite of tests | |
that run in CI and give me confidence things are working followed by an | |
automation script that publishes to npm and generates a GitHub changelog. For | |
years I&#x27;ve been using an awesome tool called | |
<a href="https://github.com/semantic-release/semantic-release">semantic-release</a> | |
(shoutout to | |
<a href="https://github.com/semantic-release/semantic-release/blob/caribou/README.md#team">the team of fantastic humans</a>) | |
to automatically release my packages.</p><p>The concept of automation is something | |
<a href="https://kentcdodds.com/blog/automation">I&#x27;ve written about in the past</a>. It&#x27;s how I got into software | |
development and I feel strongly that automation is the way we can make ourselves | |
more productive | |
(<a href="https://xkcd.com/1205">even if it takes longer to develop the automation than the time it would save us</a>). | |
If you find yourself repeatedly doing a task, see if there&#x27;s a simple way to | |
automate it. (Like | |
<a href="https://github.com/kentcdodds/hive-api">what I do for creating my kcd.im/ short urls</a> | |
+ | |
<a href="https://github.com/kentcdodds/dotfiles/blob/94c00b43354f86595647f9ff18057ff9e6469d33/.bash_profile#L61-L63">shorten</a> | |
😄, which happens to be another form of automation and productivity boost | |
because short URLs are easier/faster to give to people, and people remember them | |
better).</p><h3>Enable others</h3><p>Many of those releases of my open source projects I do are releasing code that I | |
did not write. I put forth an investment of time in helping | |
<a href="http://kcd.im/pull-request">and teaching</a> other people contribute to my | |
projects and <a href="https://github.com/all-contributors/all-contributors">do things</a> | |
to help motivate people to do so. This means that I&#x27;m able to do more because | |
other people handle a lot of project maintenance for me so I can do other | |
things.</p><h3>Don&#x27;t answer the same question twice</h3><p>I learned early on that people ask me repeat questions early on. I like to give | |
them answers, but I also found out quickly that | |
<a href="http://kcd.im/no-time">I don&#x27;t have time to answer everyone</a> and it&#x27;s a bit | |
frustrating to answer the same question multiple times. This is why having | |
<a href="https://kentcdodds.com/blog">an active blog</a> and <a href="http://kcd.im/ama">an AMA</a> are super helpful.</p><p>If someone asks me a question, 99% of the time I&#x27;ll ask them to ask it on my | |
AMA. If I get the same question many times, then I&#x27;ll make it the subject of a | |
DevTip or blog. Having multiple places/formats I can go to answer people&#x27;s | |
questions in public does four things:</p><ol><li>Allows me to answer their question</li><li>Allows others to see that answer (increases the impact of the value I&#x27;m | |
creating)</li><li>Gives me motivation to give them a higher quality answer.</li><li>Gives me a link to share with the next person who asks (which is way faster | |
than writing it out again)</li></ol><p>I guess it also contributes to the illusion that I&#x27;m doing more and I&#x27;m more | |
productive. I&#x27;m sure you answer a lot of questions as well, but how does anyone | |
know if you don&#x27;t share?</p><h3>Avoiding Burnout</h3><p>When I tweeted that this would be the subject of today&#x27;s newsletter, my good | |
friend <a href="https://twitter.com/markdalgleish">Mark Dalgleish</a> | |
<a href="https://twitter.com/markdalgleish/status/1038922626826031104">responded</a>:</p><blockquote><p><em>Because you haven&#x27;t burned out yet?</em></p></blockquote><p>I honestly don&#x27;t think that I&#x27;ve ever truly burned out. I&#x27;ve only been doing | |
this software thing professionally for ~4 years, so maybe that&#x27;s why. I&#x27;ve | |
definitely burned out on specific projects or frameworks, but I&#x27;ve generally | |
been able to keep moving and doing things that keep me excited and provide value | |
to the world while taking care of myself and my relationships.</p><p>I should probably do this subject better justice in another blog post, but I&#x27;ll | |
just say that in general what I do to avoid burnout is to not do stuff I don&#x27;t | |
have to do or want to do. I&#x27;ve learned and internalized that I don&#x27;t owe anyone | |
anything unless I&#x27;ve made an actual commitment of marriage/employment/etc. So | |
while I try to be kind and helpful, at the end of the day if I can&#x27;t help, then | |
I don&#x27;t and I don&#x27;t stress over it.</p><p>For example, there are many open issues on my GitHub projects that get no | |
response from me because I&#x27;ve chosen to give my time to other things I&#x27;d rather | |
do. I do feel bad I can&#x27;t do more, but I don&#x27;t stress over it.</p><p>This subject isn&#x27;t all that simple, but that&#x27;s all I have time for (and I&#x27;m not | |
going to stress over not giving you more because I don&#x27;t owe you anything 😜 | |
#seewhatididthere).</p><h3>Hyper-focused</h3><p>I&#x27;ve listed pretty much everything I do. You may have noticed that I don&#x27;t have | |
many hobbies. This is true. I have a few things that I do for fun, but it pretty | |
much all boils down to: Family, Religion, and Coding.</p><p>Even though this is working out so far, I don&#x27;t believe this is sustainable. | |
This is one reason why I&#x27;m so excited about writing this novel for | |
<a href="https://nanowrimo.org">NaNoWriMo</a>. It&#x27;ll be a new creative outlet. And | |
hopefully by November I&#x27;ll be done with most of | |
<a href="https://twitter.com/kentcdodds/status/1038584983990849536">the HUGE things I&#x27;m working</a> | |
on so I can dedicate myself to writing 50,000 words in 30 days :)</p><p>That said, I think short bursts of hyper-focus do help me get a lot done. | |
Whether I&#x27;m hyper-focused on an <a href="http://egghead.io">egghead.io</a> course, or | |
getting something specific done at work, it helps me get things done. I&#x27;m not | |
sure how to explain it, but for me hyper-focus means that I kinda don&#x27;t think | |
about anything else for a while. When I&#x27;m not with my family or fulfilling | |
another commitment, I&#x27;m thinking about and working on this thing until it&#x27;s | |
done.</p><p>I didn&#x27;t explain that well and should probably remove this section, but I&#x27;m not | |
gonna. Maybe it&#x27;ll be helpful for someone.</p><p><strong>Continued:</strong> Turns out this was helpful to a few people so I thought I&#x27;d | |
expound on this a tiny bit (and my wife suggested that I do as well because she | |
thinks this makes a significant impact on who I am (not necessarily that it&#x27;s a | |
desirable trait).</p><p>So this necessarily isn&#x27;t a short-term kind of thing, it doesn&#x27;t prevent me from | |
sleeping well at night (though sometimes I do have trouble shutting my brain | |
off, most of the time I sleep fine). This also isn&#x27;t the same as staying focused | |
an on-task during a period of several hours when I&#x27;m trying to get something | |
done (something that I&#x27;m typically not very good at doing unless I&#x27;m <em>very</em> | |
excited about it).</p><p>This hyper-focus is pretty much that I immerse myself in the subject for a | |
period of time. As an example, since I decided that I want to write this novel a | |
few weeks ago, I&#x27;ve found a TON of content online about tools novel writers use | |
to make their books &quot;work&quot; and I&#x27;ve been consuming it at a rapid rate. It&#x27;s | |
filled my idle mind. I&#x27;m still able to turn my attention to my family, my work, | |
or courses that I&#x27;m working on, but when I&#x27;m doing mundane tasks and my brain is | |
able to think freely, it&#x27;s consumed by the idea of the novel itself and learning | |
tools that I can apply when I start writing it in November. Ask my wife. If | |
we&#x27;re not talking about something that&#x27;s actually important, then I&#x27;ll | |
inevitably turn the conversation over to the book (and she&#x27;s been | |
<a href="https://twitter.com/kentcdodds/status/1043712163490160640">extremely helpful</a>).</p><p>I don&#x27;t have tips of how you can develop this in yourself, and I&#x27;m not even sure | |
that I would recommend it. It&#x27;s just something that I do that my wife and I | |
think may have something to do with my productivity.</p><h3>Spend more time producing than consuming</h3><p>I do not spend a lot of time watching other people&#x27;s courses or reading other | |
people&#x27;s blogs/newsletters. I definitely will skim blog posts as needed, or I&#x27;ll | |
sit down and watch a few <a href="http://egghead.io">egghead.io</a> lessons or part of a | |
Frontend Master&#x27;s course when there&#x27;s something specific I need to learn. I love | |
<a href="https://gedd.ski">Dave Geddes&#x27;s</a> mastery games on | |
<a href="https://www.gridcritters.com">CSS grid</a> and | |
<a href="https://flexboxzombies.com/p/flexbox-zombies">Flexbox</a>, but generally I spend a | |
bunch more time working on producing my own material/projects. I think that | |
makes me more productive as well.</p><h3>The importance of balance</h3><p>With all this talk of productivity, I should probably mention that I&#x27;ve learned | |
that it&#x27;s important to live a balanced life. Like I said, I can get pretty | |
focused on one thing, but I spend a lot of time with my family and that brings | |
me joy. Shutting down for a little bit, taking a step back, and <strong>working on | |
your relationships</strong> is where you&#x27;ll get your juice to keep going.</p><p>So <strong>the fact that I&#x27;m married and have four kids and a dog isn&#x27;t a detriment to | |
my productivity,</strong> but really <em>it&#x27;s an important part of my secret to | |
productivity.</em> They motivate me and recharge me in ways that I couldn&#x27;t | |
understand before I had them in my life.</p><h3>Conclusion</h3><p>So the reasons it appears I&#x27;m so productive is multi-facited:</p><ul><li>Lots of it is an illusion</li><li>I&#x27;m privileged to work at a place that values open knowledge sharing and | |
doesn&#x27;t limit what I do in my free time</li><li>I learn in public, thereby increasing the impact of the value that I create.</li><li>I automate mundane/time consuming/context switching tasks</li><li>I answer questions in a public forum</li><li>I spend more time producing that consuming</li></ul><p>I should say also that my wife plays a <strong>huge</strong> role in how I&#x27;m so productive. | |
However, she is a pretty private person and asked that I not talk about her much | |
publicly (except she did give me permission to say this). I couldn&#x27;t do all of | |
the things I do if it weren&#x27;t for her.</p><p>I don&#x27;t want to give the false impression that I only <em>appear</em> productive | |
either. I really do feel like I&#x27;m quite productive. But hopefully this makes my | |
productivity more realistic and attainable in your mind. I hope some of these | |
ideas help inspire you to be more productive and more importantly find more | |
happiness through your relationships. Good luck!</p><p>Subscribe to my newsletter below to get content like this delivered to your | |
inbox two weeks before it&#x27;s published to my blog:</p><p><strong>Learn more about career development from me</strong>:</p><ul><li><a href="https://kentcdodds.com/blog/why-and-how-i-started-public-speaking">Why and How I started public speaking</a></li><li><a href="https://buttondown.email/kentcdodds/archive/03761505-8609-404c-a5b7-5367013292bf">Getting Noticed and Widening Your Reach</a></li><li><a href="https://youtu.be/-qPh6I2hfjw?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Zero to 60 in Software Development: How to Jumpstart Your Career — Forward 4 Web Summit</a></li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://medium.com/@sachagreif/take-the-state-of-javascript-2018-survey-c43be2fcaa9">Take the State of JavaScript 2018 Survey!</a> — And | |
make sure to add react-testing-library in the &quot;other&quot; field for &quot;testing | |
tools.&quot; If they can add enzyme, then they should have react-testing-library as | |
well!</li><li><a href="https://www.ageekleader.com/agl-059-kent-c-dodds">A Geek Leader 059: Kent C. Dodds</a> — On | |
this podcast I talk about my career story and stuff! Should be interesting if | |
you thought this blog post was interesting :)</li><li><a href="https://github.com/mattphillips/jest-expect-message">jest-expect-message</a> by | |
<a href="https://twitter.com/mattphillipsio">Matt Phillips</a>: Add custom message to | |
Jest expects 🃏🗯</li><li><a href="https://github.com/jest-community/jest-extended">jest-extended</a>: Additional | |
Jest matchers 🃏💪</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-i-am-so-productive">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Getting Noticed and Widening Your Reach]]></title> | |
<description><![CDATA[This last week I had three people complaining to me (in individual interactions) | |
that they create cool things, but what they create is not noticed because they | |
don't have a following on Twitter or otherwise. In at least one of the cases | |
there was a…]]></description> | |
<link>https://kentcdodds.com/blog/getting-noticed-and-widening-your-reach</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/getting-noticed-and-widening-your-reach</guid> | |
<pubDate>Mon, 17 Sep 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>This last week I had three people complaining to me (in individual interactions) | |
that they create cool things, but what they create is not noticed because they | |
don&#x27;t have a following on Twitter or otherwise. In at least one of the cases | |
there was a fair amount of bitterness. An attitude of: &quot;If you create something | |
it becomes popular by virtue of you being so popular. If I create something, it | |
goes unnoticed even if it&#x27;s better than what you created.&quot;</p><p>While this may be true, the attitude bothers me. I&#x27;d be classified in the | |
&quot;popular&quot; crowed, so maybe that&#x27;s why it bothers me, but it also bothers me | |
because it feels like | |
<a href="https://www.lds.org/general-conference/1989/04/beware-of-pride">&quot;pride from the bottom looking up&quot;</a> | |
or could also come from | |
<a href="https://mindsetonline.com/whatisit/about">a fixed mindset</a>. I wasn&#x27;t born with | |
a twitter following (though I was born into privilege which makes a non-trivial | |
impact, and I&#x27;ll address that later). I worked for this by creating things and | |
doing things to get noticed even before I had the following. This is true of | |
most people with a wide reach. In today&#x27;s newsletter, I&#x27;d like to share with you | |
some things that I&#x27;ve seen be an effective way to get your work noticed and | |
widen your reach.</p><p>There&#x27;s not one thing I&#x27;ve done to get noticed and widen my reach. And there&#x27;s | |
also not a sequence. It&#x27;s been a mix of several things (including hard work, | |
luck, timing, and kindness of others) that have helped me to do this. Depending | |
on what you&#x27;re trying to get noticed for, there are different things you can do, | |
so I&#x27;ll focus on a few and hopefully that&#x27;ll touch on what you&#x27;re doing...</p><h3><a href="https://github.com/kentcdodds">Open source</a></h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/1ac7db0cd66099506485ca721862a6dd/a296c/typing.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:66.625%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="a person touch typing on a laptop" title="a person touch typing on a laptop" src="https://kentcdodds.com/static/1ac7db0cd66099506485ca721862a6dd/a296c/typing.jpg" srcSet="https://kentcdodds.com/static/1ac7db0cd66099506485ca721862a6dd/d2274/typing.jpg 259w,https://kentcdodds.com/static/1ac7db0cd66099506485ca721862a6dd/34151/typing.jpg 518w,https://kentcdodds.com/static/1ac7db0cd66099506485ca721862a6dd/a296c/typing.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><h3><a href="https://github.com/kentcdodds/genie">geniejs 🧞</a></h3><p>My first personal open source project that I wanted to get noticed was | |
<a href="https://github.com/kentcdodds/genie">geniejs</a>. | |
<a href="https://github.com/kentcdodds/genie/commit/18db942457d8e7855a3afbb7345e9f8aa5c5a8ce">The first commit</a> | |
was back in 2013. I can&#x27;t remember exactly how many followers I had, but at the | |
time | |
<a href="https://twitter.com/search?l=&amp;q=from%3Akentcdodds%20since%3A2013-08-01%20until%3A2013-08-31&amp;src=typd">I was tweeting</a> | |
using <a href="https://friendsplus.me">Friends+ me</a> (because I thought Google+ was the | |
bomb) and of all my tweets that month, only one got a single favorite. Needless | |
to say, I wasn&#x27;t well known or popular.</p><p>And honestly, my project didn&#x27;t get super popular at the time either. However, | |
that didn&#x27;t stop me from working on getting it noticed. I presented it at my | |
work&#x27;s hack night around that time (it&#x27;s actually | |
<a href="https://github.com/kentcdodds/ama/issues/1">how I got my first fulltime job at Domo</a>), | |
I built | |
<a href="http://kentcdodds.github.io/genie/workshop">a sweet in-browser workshop</a> and | |
used it in | |
<a href="https://www.meetup.com/UtahJS-Orem-Meetup/events/156148202">my first meetup talk</a>, | |
I then lucked out and got a conference (MidwestJS 2014) to accept me to give | |
that talk there (I only was able to speak because another speaker canceled).</p><p>Here are some takeaways from this experience: It never got very popular or | |
widely used (it&#x27;s still not, despite my level of reach, though | |
<a href="https://github.com/CompuIves/codesandbox-client/blob/76815f14c90e85c95c604624466fe2d50c995204/packages/app/src/app/pages/Sandbox/QuickActions/index.js">it is used by codesandbox!</a>). | |
Despite this it gave me opportunities to improve my skills, show my skills, get | |
a job, and speak at a meetup and conference. This was primarily a combination of | |
luck, timing, assistance (my friend | |
<a href="https://twitter.com/iammerrick">Merrick Christensen</a> should be thanked for | |
helping me turn this into a job opportunity), courage (to propose my library as | |
the subject for a talk at a meetup and conference), and hard work/persistence (I | |
documented the library very well and built a great learning experience).</p><h3><a href="https://github.com/formly-js/angular-formly">angular-formly</a></h3><p>About a year after I started work on geniejs (when I still did not have much by | |
way of a twitter following, etc.), I was moonlighting at a small startup that | |
had a pretty basic CRUD app with a TON of forms. I looked around at the | |
AngularJS form libraries, found one I really liked and before long | |
<a href="https://github.com/formly-js/angular-formly/pull/17">I made my first pull request to angular-formly</a>. | |
At the time, it wasn&#x27;t a terribly popular project itself, but I needed it and it | |
satisfied most of my use cases.</p><p>In the weeks that followed I made more and more pull requests and before too | |
long, the project maintainer (who was no longer using the project) asked me if I | |
wanted to take the project over as the maintainer. I accepted and began the work | |
of improving the project and building a community.</p><p>Honestly, at the time I wasn&#x27;t really thinking that&#x27;s what I was doing. A lot of | |
my concern was the excitement of hearing that people were using something that I | |
built and a desire for my library to be the best in its class. Definitely not | |
the most pure motivations, but my results were positive and within a few months, | |
<a href="https://npm-stat.com/charts.html?package=angular-formly&amp;from=2015-01-01&amp;to=2018-09-01">downloads quadrupled</a> | |
and to my knowledge, angular-formly is still the number one most downloaded | |
AngularJS forms library.</p><p>So what did I do to build such popularity of the project so quickly? Well, one | |
thing I did (which I don&#x27;t recommend) was I sorta sold my soul to the project. I | |
allowed myself to get carried away in answering issues as if they were paid | |
support requests. I prided myself on responding to issues in seconds and having | |
a fix pushed out in minutes. This was a lot of fun, but it did put strain on my | |
important relationships and my mental health, which is why I don&#x27;t recommend it.</p><p>I&#x27;ve found that you can build a strong and positive community without | |
sacrificing your well-being and relationships. That&#x27;s a subject for another blog | |
post. Just know that working on building a community by | |
<a href="https://twitter.com/kentcdodds/status/582620905433489408">creating fantastic documentation</a>(including | |
<a href="http://docs.angular-formly.com/docs/learn-angular-formly">several free egghead.io lessons</a>), | |
<a href="https://kentcdodds.com/blog/first-timers-only">encouraging contribution</a>, and | |
<a href="https://github.com/all-contributors/all-contributors">recognizing and trusting contributors</a> | |
is an important part of getting adoption and recognition for your open source | |
work.</p><p>Other things that I did which were significant help were similar to what I did | |
with geniejs: I spoke at meetups and conferences (my talk at MidwestJS 2015 was | |
about angular-formly, and I gave the same talk at the first ng-nl conference). I | |
worked on making the documentation and learning materials fantastic. I ensured | |
the project satisfied the use cases it needed to while attempting to avoid | |
overcomplexity (so it was something people would <em>want</em> to use). I also reached | |
out to <em>relevant</em> newsletters to invite them to check it out and reference it in | |
their newsletter. Oh, and I tweeted about it | |
<a href="https://twitter.com/search?q=from%3Akentcdodds%20formly&amp;src=typd">a lot</a>.</p><p>It took a lot of work, but my work was recognized and appreciated and the | |
success there was a major talking point in my interview with my future boss&#x27;s | |
boss at PayPal which probably helped me get hired.</p><p><strong>Something I did NOT do</strong> is spam dozens of &quot;influencers&quot; out of the blue on | |
twitter asking them to use and promote a library which for them was probably | |
irrelevant because they probably didn&#x27;t need or have a use case for anyway. I | |
have dozens of people per week asking me to review the things they&#x27;ve built or | |
written. This really only bothers me when it&#x27;s clear that I&#x27;m one of tens of | |
others to whom they&#x27;ve sent the same message, but it&#x27;s also ineffective because | |
often these &quot;influencers&quot; either don&#x27;t have the time to review them or the | |
project may not have any relevance to them.</p><p>I also did <em>not</em> attempt to throw shade at alternatives or downplay the value of | |
their abstractions. Being unkind to the hard work of other people is a very poor | |
way to promote your project and has no good place in the world.</p><h3><a href="https://kentcdodds.com/talks">Speaking</a> &amp; <a href="https://kentcdodds.com/workshops">Teaching</a></h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/127992efad005a7454c557a8815c4162/a296c/0.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:66.625%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="me teaching people at a workshop" title="me teaching people at a workshop" src="https://kentcdodds.com/static/127992efad005a7454c557a8815c4162/a296c/0.jpg" srcSet="https://kentcdodds.com/static/127992efad005a7454c557a8815c4162/d2274/0.jpg 259w,https://kentcdodds.com/static/127992efad005a7454c557a8815c4162/34151/0.jpg 518w,https://kentcdodds.com/static/127992efad005a7454c557a8815c4162/a296c/0.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>I recently published a blog post called | |
<a href="https://kentcdodds.com/blog/why-and-how-i-started-public-speaking">Why and How I started public speaking</a>. | |
I&#x27;m just going to link you to that story rather than re-iterate the whole thing | |
here. But I do want to tell how I got started with creating content for | |
<a href="http://egghead.io">egghead.io</a> as that made a big influence on widening my | |
reach.</p><p>Back in June 2014, I spoke at | |
<a href="https://www.meetup.com/AngularJS-Utah/events/173788512">AngularJS Utah meetup</a> | |
about something I had been learning about in a school project | |
(<a href="https://youtu.be/vIGZxeQUUFU?t=1m51s">you can watch the talk here!</a>). Not long | |
later, <a href="https://twitter.com/johnlindquist">John Lindquist</a> (co-founder and | |
original instructor on <a href="http://egghead.io">egghead.io</a>) watched the recording | |
and emailed me inviting me to turn the workshop into a course.</p><p>This lead into an incredible positive relationship. Being an instructor on a | |
platform like <a href="http://egghead.io">egghead.io</a> gives you automatic authority in | |
the minds of many people. It did take some time and consistency with creating | |
content on <a href="http://egghead.io">egghead.io</a>, but after some time, the following | |
started to grow (as did my royalties which have been sufficient enough to pay my | |
monthly home mortgage here in Utah for a couple years now).</p><p><strong>Here&#x27;s the takeaway:</strong> If my talk had not been recorded, John would not have | |
seen the value I created that night at the meetup and I could have missed or | |
delayed my <a href="http://egghead.io">egghead.io</a> opportunity. You are constantly | |
creating value. A conversation with a co-worker, a meetup talk, a realization | |
after hours of working on a hard to solve bug. <strong>The trick is to take those | |
value creating experiences and make them public.</strong>This is where my | |
<a href="http://kcd.im/devtips">DevTips with Kent</a> and my | |
<a href="http://kcd.im/tech-chats">Tech Chats</a> come from. Both of those activities widen | |
my reach.</p><p>Just over a year after I started with <a href="http://egghead.io">egghead.io</a> (in | |
September 2015), after I had established myself a bit with egghead, I was | |
listening to episode 178 of <a href="https://devchat.tv/js-jabber">JavaScript Jabber</a>: | |
<a href="https://devchat.tv/js-jabber/178-jsj-tech-education-and-the-business-of-running-front-end-masters-with-marc-grabanski">Tech Education and The Business of Running Front End Masters with Marc Grabanski</a>. | |
I had met Marc at my first MidwestJS where we were both speakers and thought he | |
was a pretty cool dude. I knew of Frontend Masters and had a great deal of | |
respect for Marc and his company. So I reached out to him:</p><blockquote><p><em>Hey dude,<br/>I&#x27;m listening to your JSJabber episode and I&#x27;ve been wanting to do a FEM | |
workshop for a long time. I&#x27;ve been trying to think about what I could | |
contribute and something that I&#x27;m really into is open source. I have a growing | |
list of open source libraries</em> <a href="https://www.npmjs.com/~kentcdodds"><em>on npm</em></a> &gt; | |
<em>and</em> <a href="https://github.com/kentcdodds"><em>GitHub</em></a> <em>and I&#x27;m the owner and | |
maintainer of</em> &gt; | |
<a href="https://github.com/formly-js/angular-formly"><em>angular-formly</em></a> &gt; <em>which has | |
grown in popularity.</em></p><p><em>I also have an</em> &gt; | |
<a href="https://egghead.io/series/how-to-write-an-open-source-javascript-library"><em>egghead.io series</em></a> &gt; | |
<em>on the subject. But, as you know, a workshop format would make it pretty | |
useful.</em></p><p><em>I think that we could get more people into open source if they just knew how | |
to get started. I believe you&#x27;re interested in open source as well with your | |
jQuery UI Datepicker experience. I think you could be pretty jazzed about this | |
workshop.<br/>What do I need to do to make this happen?</em></p><p><em>Thanks!<br/>Kent</em></p></blockquote><p>To which Marc responded:</p><blockquote><p><em>Sounds awesome! When were you thinking?</em></p></blockquote><p>And so began my relationship with Frontend Masters. I now have | |
<a href="https://frontendmasters.com/teachers/kentcdodds">six courses (almost 7) on Frontend Masters</a>. | |
<strong>The takeaway here</strong> is courage. I&#x27;m mostly sharing my success stories in this | |
blog post, but you gotta know that there were plenty of rejections as well. But | |
it&#x27;s ok! Have the courage to ask.</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:500px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/66677db6a8e7d983187b70ee297223b6/48a11/1.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:57.00000000000001%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="You miss 100% of the shots you don&#x27;t take -Wayne Gretzky — Michael Scott" title="You miss 100% of the shots you don&#x27;t take -Wayne Gretzky — Michael Scott" src="https://kentcdodds.com/static/66677db6a8e7d983187b70ee297223b6/48a11/1.jpg" srcSet="https://kentcdodds.com/static/66677db6a8e7d983187b70ee297223b6/d2274/1.jpg 259w,https://kentcdodds.com/static/66677db6a8e7d983187b70ee297223b6/48a11/1.jpg 500w" sizes="(max-width: 500px) 100vw, 500px"/> | |
</a> | |
</span></p><h3>Podcasting</h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/78c3788a695e59db293a3d933669c038/a296c/2.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:66.625%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Microphone" title="Microphone" src="https://kentcdodds.com/static/78c3788a695e59db293a3d933669c038/a296c/2.jpg" srcSet="https://kentcdodds.com/static/78c3788a695e59db293a3d933669c038/d2274/2.jpg 259w,https://kentcdodds.com/static/78c3788a695e59db293a3d933669c038/34151/2.jpg 518w,https://kentcdodds.com/static/78c3788a695e59db293a3d933669c038/a296c/2.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>I have to mention my podcasts as a form of widening my reach and getting | |
noticed. Back in November 2014, Todd Motto and I kicked off our bi-weekly Google | |
Hangouts on Air called <a href="https://angularair.com">&quot;Angular Air&quot;</a> with | |
<a href="https://youtu.be/LG9VkCDbte0">&quot;Angular Air Episode 0: The Angular Team on 1.3 &amp; 2.0&quot;</a>. | |
We didn&#x27;t plan on making it a podcast. We just wanted to chat every other week | |
between ourselves and maybe a guest or two about AngularJS. We figured it&#x27;d be a | |
good way to talk to cool people and help the community.</p><p>This first episode was so well received that I quickly took the audio from the | |
YouTube video and created <a href="http://audio.angularair.com">a podcast</a> which quickly | |
started getting thousands of downloads per week. Todd was pretty busy, and I | |
wanted to go weekly, so I took it over and started getting more and more guests.</p><p>What I quickly discovered is that you can get really cool people to freely give | |
you an hour of their time when you can promise that the value they&#x27;re creating | |
by talking with you will have an increased impact on thousands of developers all | |
over the world.</p><p>When I made the transition from AngularJS to React at PayPal, I handed off | |
Angular Air to <a href="https://twitter.com/jeffwhelpley">Jeff Whelpley</a> and started | |
<a href="https://javascriptair.com">JavaScript Air</a> with a panel of developers that&#x27;d | |
knock your socks off and | |
<a href="https://javascriptair.com/episodes/2015-12-09">a first episode</a> with the | |
creator of JavaScript himself, <a href="https://twitter.com/brendaneich">Brendan Eich</a>. | |
I was able to get these folks because they&#x27;re just awesome people and I had | |
proven myself to them with my experience as a podcaster with Angular Air and I | |
could promise them that their time investment would have a great positive impact | |
on the thousands of developers who would listen to the show. And when you are | |
able to associate yourself with people like this, their followers will notice | |
you as well and many become your followers. <strong>I think that of all things, my | |
podcasts had the biggest impact on my reach while I was running them.</strong></p><p>The show did prove to take more time than I had, so I did eventually | |
<a href="https://kentcdodds.com/blog/sunsetting-javascript-air">Sunset JavaScript Air</a>. But maybe one day I&#x27;ll | |
start it back up again. It was awesome.</p><blockquote><p><em>I&#x27;ll just add here really quick that I&#x27;m also way more likely to be able to | |
give you an hour of my time if I know that the value we create will be spread | |
to impact more people. I&#x27;m always happy to join you on a podcast.</em> &gt; | |
<a href="https://kentcdodds.com/appearances"><em>Here&#x27;s a list of podcasts I&#x27;ve been a guest on in the past</em></a><em>.</em></p></blockquote><h3><a href="http://kcd.im/news">Newslettering</a> &amp; <a href="https://kentcdodds.com/blog">Blogging</a></h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/cf7cb5246dbabe7cd69c1b78b36628c0/a296c/3.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:66.625%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="A notebook and a pen" title="A notebook and a pen" src="https://kentcdodds.com/static/cf7cb5246dbabe7cd69c1b78b36628c0/a296c/3.jpg" srcSet="https://kentcdodds.com/static/cf7cb5246dbabe7cd69c1b78b36628c0/d2274/3.jpg 259w,https://kentcdodds.com/static/cf7cb5246dbabe7cd69c1b78b36628c0/34151/3.jpg 518w,https://kentcdodds.com/static/cf7cb5246dbabe7cd69c1b78b36628c0/a296c/3.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>This newsletter email is being sent as the last email to complete a full year of | |
weekly newsletters! Without fail, I&#x27;ve sent out an email every week for an | |
entire year! Two weeks after I send you this newsletter, I publish it to my blog | |
where it gets several hundred or thousand more views/reads. That amounts to over | |
50 value-adding blog posts! I&#x27;m pretty amazed that I&#x27;ve been able to keep it up | |
and I have no plans to stop.</p><p>At this moment I have over 7,000 subscribers to my newsletter. But I didn&#x27;t | |
start with that. Granted, when I started my newsletter I already had a pretty | |
strong twitter following, but it&#x27;s consistency which has gotten me the | |
subscribers I have. I still have &gt;50% open rate (which I&#x27;m told is very good). | |
You subscribers have arrived and stayed at this weekly newsletter because of the | |
value that I am creating and delivering to your inbox consistently every week. | |
Hopefully some of you were slightly disappointed when this particular issue came | |
12 hours later than normal 😅</p><p>I&#x27;ve learned that consistency is important with a blog. With the exception of | |
this newsletter (which has taken many many hours), my newsletters take about an | |
hour of my time to produce. I can write these during my work hours because | |
several of you subscribers are PayPal employees and my boss thinks it&#x27;s cool | |
that I&#x27;m helping educate PayPal every week as well as the community at large.</p><h3><a href="https://twitter.com/kentcdodds">Tweeting</a></h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/6470e68478425e3554fc456076a81346/a296c/4.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:71.375%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="blue bird" title="blue bird" src="https://kentcdodds.com/static/6470e68478425e3554fc456076a81346/a296c/4.jpg" srcSet="https://kentcdodds.com/static/6470e68478425e3554fc456076a81346/d2274/4.jpg 259w,https://kentcdodds.com/static/6470e68478425e3554fc456076a81346/34151/4.jpg 518w,https://kentcdodds.com/static/6470e68478425e3554fc456076a81346/a296c/4.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>Most of my reach is found on twitter. Twitter is a funny and unique place that | |
we all love to hate (and most of us secretly love). I&#x27;ve found the best way to | |
be effective on twitter is by following people who (re)tweet | |
valuable/interesting/relevant stuff, and by (re)tweeting stuff I find | |
valuable/interesting/relevant. I have no shame in retweeting or promoting | |
something that&#x27;s valuable even if it also happens to be promoting myself or if | |
the tweeter says something nice about me. I use twitter as a platform to share | |
ideas, ask questions, and promote things that I think are important/people will | |
find useful.</p><p>As you might imagine, I have dozens (hundreds?) of DMs sitting in my twitter DM | |
requests that I&#x27;ve never found the time to respond to. If you&#x27;re sitting in | |
there waiting I&#x27;m so sorry. Finding time to respond to every DM is impossible, | |
which leads me to my next point...</p><h3><a href="https://github.com/kentcdodds/ama">Ask Me Anything</a></h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/1b139687e84f925c5b96805080d56970/a296c/5.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:50.625%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="mountain" title="mountain" src="https://kentcdodds.com/static/1b139687e84f925c5b96805080d56970/a296c/5.jpg" srcSet="https://kentcdodds.com/static/1b139687e84f925c5b96805080d56970/d2274/5.jpg 259w,https://kentcdodds.com/static/1b139687e84f925c5b96805080d56970/34151/5.jpg 518w,https://kentcdodds.com/static/1b139687e84f925c5b96805080d56970/a296c/5.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>In July 2015, I noticed <a href="https://twitter.com/sindresorhus">Sindre Sorhus</a> | |
created a new repo called <a href="https://github.com/sindresorhus/ama">ama</a>. It&#x27;s | |
basically a GitHub repo where people can ask him anything that they like to (he | |
doesn&#x27;t make any guarantees that he&#x27;ll answer anything though). I thought this | |
was a great solution to a problem I was beginning to have: Answering the same | |
questions repeatedly.</p><p>So the next day I forked his repo and started my own. I now have over 450 | |
questions answered and regularly direct people there to get their questions | |
answered or ask new questions. This is another way that I&#x27;m able to increase the | |
impact of the value that I create. By making my answers public, searchable, and | |
referenceable, I&#x27;m able to answer way more questions than I could otherwise | |
<a href="http://kcd.im/no-time">have time for</a> and help way more people as well. I do | |
still have a little trouble answering everything very quickly, but I generally | |
do get to everything eventually in one form or another.</p><h3>The Unquestionable Influence of Privilege</h3><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/1be352ff7cc4d5b83f871ae957650ced/a296c/6.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:66.625%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="a heart on grass" title="a heart on grass" src="https://kentcdodds.com/static/1be352ff7cc4d5b83f871ae957650ced/a296c/6.jpg" srcSet="https://kentcdodds.com/static/1be352ff7cc4d5b83f871ae957650ced/d2274/6.jpg 259w,https://kentcdodds.com/static/1be352ff7cc4d5b83f871ae957650ced/34151/6.jpg 518w,https://kentcdodds.com/static/1be352ff7cc4d5b83f871ae957650ced/a296c/6.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>We all need to acknowledge something very important: Privilege. If you&#x27;ve not | |
heard Kyle Simpson talk about | |
<a href="https://twitter.com/search?q=%23privilegeawareness&amp;src=typd">#PrivilegeAwareness</a>, | |
give the first part of <a href="https://youtu.be/wJEX2FgNYLg?t=46s">this talk</a> a quick | |
watch. I have similar privileges that Kyle mentions. Incidentally, a few hours | |
after Kyle gave his talk, | |
<a href="https://youtu.be/-qPh6I2hfjw?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf&amp;t=2m33s">I gave a talk</a> | |
at the same conference where I talk about my privilege as an important thing to | |
acknowledge.</p><p>Yes, I worked really hard to get noticed and gain the reach that I have. But I | |
would be remiss to not acknowledge the fact that while I was working so hard, I | |
didn&#x27;t also have to work against prejudices due to the color of my skin or | |
gender. I grew up with access to technology and a safe and well-off family that | |
gave me opportunities to excel and succeed.</p><p>I think it&#x27;s also important for each of us to be aware of our privilege so we | |
can work on solving societal problems and actively battle (un)conscious bias as | |
well as have empathy for those who are in less privileged positions than | |
ourselves. To <a href="https://youtu.be/8Nvg-MMtN_A">&quot;lend our privilege&quot;</a> (that&#x27;s a | |
link to a fantastic talk by <a href="https://twitter.com/anjuan">Anjuan Simmons</a> which | |
you should watch right now) in an effort to make the world a better place.</p><h3>Conclusion</h3><p>I sincerely hope this was helpful. I wasn&#x27;t born with a wide reach. And I | |
haven&#x27;t resented the reach that I have as I have been developing it. I&#x27;ve worked | |
hard to produce useful content for people who in turn follow me because they | |
want more of that value which we all create and I capture and disseminate. You | |
can do this too. Don&#x27;t despair. Keep working at it. Be happy with where you are | |
and the direction you&#x27;re going. You can do it! Good luck!</p><p><strong>Learn more about career development from me</strong>:</p><ul><li><a href="https://youtu.be/-qPh6I2hfjw?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Zero to 60 in Software Development: How to Jumpstart Your Career — Forward 4 Web Summit</a></li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://youtu.be/z4DNlVlOfjU?list=PLV5CVI1eNcJi8sor_aQ2AzOeQ3On3suOr">Testing React with Kent C. Dodds and Jack Franklin</a> — A | |
<a href="https://github.com/kentcdodds/ama/issues/125">tech chat</a> with myself and | |
<a href="https://twitter.com/jack_franklin">Jack Franklin</a> talking about testing | |
React. We cover a lot of subjects that you may find interesting!</li><li><a href="https://github.com/facebook/react/issues/13525">React Fire 🔥🔥🔥</a> — An | |
initiative to modernize react-dom</li><li><a href="https://www.netlify.com/blog/2018/08/29/using-the-react-devtools-profiler-to-diagnose-react-app-performance-issues">Using the React DevTools Profiler to Diagnose React App Performance Issues</a>- | |
A great blog post by <a href="https://twitter.com/swyx">Shawn Wang</a> about an awesome | |
new feature in the React DevTools!</li><li><a href="https://ui.reach.tech">ui.reach.tech</a> — An awesome collection of highly | |
accessible React components by my friend | |
<a href="https://twitter.com/ryanflorence">Ryan Florence</a> (he&#x27;s also giving | |
<a href="https://reach.tech/workshops">Advanced React trainings</a> all over the US | |
starting in October).</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/getting-noticed-and-widening-your-reach">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Make Impossible States Impossible]]></title> | |
<description><![CDATA[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…]]></description> | |
<link>https://kentcdodds.com/blog/make-impossible-states-impossible</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/make-impossible-states-impossible</guid> | |
<pubDate>Mon, 10 Sep 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>This is a phrase I first heard from | |
<a href="https://twitter.com/DavidKPiano">David Khourshid</a> in his talk at React Rally | |
2017 <a href="https://youtu.be/VU1NKX6Qkxc">Infinitely Better UIs with Finite Automata</a>: | |
&quot;Make impossible states impossible&quot; (super great talk by the way, and | |
<a href="https://github.com/davidkpiano/xstate">xstate</a> is awesome, and David is too). | |
Googling around it looks like it&#x27;s a pretty popular phrase in the | |
<a href="http://elm-lang.org">Elm</a> community, though I&#x27;m not sure who said it first.</p><p>To illustrate what this means, let&#x27;s checkout at | |
<a href="https://codesandbox.io/s/j71ljpvvww">a very simple example</a>:</p><pre><code class="language-jsx">&lt;Alert&gt;Just FYI&lt;/Alert&gt; | |
&lt;Alert success&gt;It worked!&lt;/Alert&gt; | |
&lt;Alert warning&gt;Head&#x27;s up&lt;/Alert&gt; | |
&lt;Alert danger&gt;Watch out!&lt;/Alert&gt; | |
</code></pre><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:652px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/d6d585ac8b7bcfc4b83fb18738e8c504/c2dea/0.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:88.34355828220859%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Rendered example" title="Rendered example" src="https://kentcdodds.com/static/d6d585ac8b7bcfc4b83fb18738e8c504/c2dea/0.png" srcSet="https://kentcdodds.com/static/d6d585ac8b7bcfc4b83fb18738e8c504/f4a45/0.png 259w,https://kentcdodds.com/static/d6d585ac8b7bcfc4b83fb18738e8c504/ef0f6/0.png 518w,https://kentcdodds.com/static/d6d585ac8b7bcfc4b83fb18738e8c504/c2dea/0.png 652w" sizes="(max-width: 652px) 100vw, 652px"/> | |
</a> | |
</span></p><p>You may have used or written a component that has this kind of API. It&#x27;s nice | |
and clean (in case you&#x27;re unfamiliar, in JSX, a non-assigned prop like that is | |
the same as assigning it to the value &quot;true&quot;, so <code>success</code> is the same as | |
<code>success={true}</code>).</p><p>Here&#x27;s where this kind of API falls over. What should I render with this?</p><pre><code class="language-jsx">&lt;Alert success warning&gt; | |
It worked! | |
&lt;/Alert&gt; | |
</code></pre><p>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&#x27;t know | |
what they&#x27;re doing? (tip: that last one is <em>NOT</em> what you should do).</p><p>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.</p><p>So what do we do with our simple example? Whelp, all these different props | |
represent is the <em>type</em> of alert that should be rendered. So what if instead of | |
simply accepting the prop itself, we accept a <code>type</code> prop?</p><pre><code class="language-jsx">&lt;AlertBetter&gt;Just FYI&lt;/AlertBetter&gt; | |
&lt;AlertBetter type=&quot;success&quot;&gt;It worked!&lt;/AlertBetter&gt; | |
&lt;AlertBetter type=&quot;warning&quot;&gt;Head&#x27;s up&lt;/AlertBetter&gt; | |
&lt;AlertBetter type=&quot;danger&quot;&gt;Watch out!&lt;/AlertBetter&gt; | |
</code></pre><p>Now it&#x27;s impossible to have more states than one because there are only three | |
valid values for the <code>type</code> prop (well, four if you count <code>undefined</code>)! It&#x27;s | |
easier to maintain, easier to explain/understand, and harder to mess up. | |
Everyone wins!</p><h3>Conclusion</h3><p>There are various ways to do this effectively and converting a boolean value to | |
<a href="https://en.wikipedia.org/wiki/Enumerated_type">an enum</a> is only one such | |
mechanism. It can and does get more complicated, but the concept can seriously | |
simplify your component&#x27;s and application&#x27;s state. This is why I recommend you | |
give <a href="https://twitter.com/DavidKPiano">David&#x27;s</a> talk a watch | |
(<a href="https://youtu.be/VU1NKX6Qkxc">here it is again</a>). And give | |
<a href="https://github.com/davidkpiano/xstate">xstate</a> a solid look as well. There&#x27;s | |
some good ideas in that! Good luck!</p><p>P.S. After publishing this, several people noted that this phrase was the title | |
of <a href="https://youtu.be/IcgmSRJHu_8">a talk</a> from | |
<a href="https://twitter.com/rtfeldman">Richard Feldman</a> at Elm Conf. There&#x27;s also | |
<a href="https://youtu.be/P7dTPoxCg4w">a talk</a> by | |
<a href="https://twitter.com/ryyppy">Patrick Stapfer</a> from ReasonML Munich Meetup. You | |
may also be interested to check out | |
<a href="https://github.com/stereobooster/pragmatic-types/blob/master/posts/making-impossible-states-impossible.md">a similar blog post</a> | |
by <a href="https://twitter.com/stereobooster">@stereobooster</a>.</p><p>P.S.P.S. Further revelation about the origin of a similar phrase:</p><p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">I did coin the term &quot;make illegal states unrepresentable&quot;, but the idea is of course much older. The phrase &quot;Minsky compliant&quot; surely gives me too much credit, but at least it sounds more positive than the concept of the &quot;Minsky moment&quot;.</p>— Yaron Minsky (@yminsky) <a href="https://twitter.com/yminsky/status/1034947939364425731">August 29, 2018</a></blockquote></p><p><strong>Learn more about React from me</strong>:</p><ul><li><a href="http://kcd.im/beginner-react">The Beginner&#x27;s Guide to React</a></li><li><a href="http://kcd.im/advanced-react">Advanced React Component Patterns</a> (also on | |
<a href="https://frontendmasters.com/courses/advanced-react-patterns">Frontend Masters</a>).</li><li><a href="https://egghead.io/lessons/react-confidently-ship-production-react-apps">Confidently Ship Production React Apps</a> — <strong>A | |
30 talk on egghead</strong> (30 minutes... weird right?!) about testing React | |
components.</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="http://babeljs.io/blog/2018/08/27/7.0.0">Babel 7 Released</a> — Congratulations | |
to the Babel team for releasing Babel 7! I wrote the portion on | |
<code>babel-plugin-macros</code>!</li><li><a href="https://youtube.com/watch?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf&amp;v=AiJ8tRRH0f8">Simply React</a>: | |
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&#x27;ll enjoy this!</li><li><a href="https://github.com/fastpack/fastpack"><code>fastpack</code></a> - a JavaScript bundler | |
(like webpack, but with fewer features at the moment) that is BLAZING FAST | |
🔥🔥🔥🔥 (bundles 1000 modules in &lt; 1 second!)</li><li><a href="https://byteconf.com">Byteconf React</a> — 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&#x27;re building a community of developers around the | |
world — see you there?</li></ul><h3>Bonus:</h3><p>Here&#x27;s a part from my section in the babel 7 blog post that didn&#x27;t make the | |
final cut but I thought you&#x27;d enjoy:</p><p>Here&#x27;s a very simple example of a custom macro that swaps <code>line</code>and <code>column</code> | |
imports with the line or column where it appears in the code:</p><pre><code class="language-js">// line-column.macro | |
module.exports = createMacro(lineColumnMacro) | |
function lineColumnMacro({references, babel}) { | |
references.line.forEach(referencePath =&gt; { | |
const num = referencePath.node.loc.start.line | |
referencePath.replaceWith(babel.types.numberLiteral(num)) | |
}) | |
references.column.forEach(referencePath =&gt; { | |
const num = referencePath.node.loc.start.column | |
referencePath.replaceWith(babel.types.numberLiteral(num)) | |
}) | |
} | |
</code></pre><p>And then this code:</p><pre><code>`import {line, column} from &#x27;./line-column.macro&#x27; | |
` | |
``console.log(`we&#x27;re at ${line}:${column}`) | |
console.log(`and now we&#x27;re at ${line}:${column}`)`` | |
</code></pre><p>This will be transpiled into:</p><pre><code class="language-js">console.log(`we&#x27;re at ${3}:${32}`) | |
console.log(`and now we&#x27;re at ${4}:${40}`) | |
</code></pre><p>And we don&#x27;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&#x27;t do everything | |
that a full babel plugin can do, but we&#x27;re only just getting started with this | |
and look forward to what the community will do with this power.</p><blockquote><p><em>Play around with the above macro example</em> &gt; | |
<a href="https://astexplorer.net/#/gist/e586bcbbf2ce35835115a7d808528c90/b64f5f025d98481ebfb93a582334c8562f7337f0"><em>on astexplorer.net</em></a><em>. | |
See if you can make it support an import called</em> <code>_lineColumn_</code><em>that is | |
replaced with the sum of line and column. Just for fun!</em></p></blockquote> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/make-impossible-states-impossible">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[How to make the most out of conferences]]></title> | |
<description><![CDATA[I spent the last week at React Rally in Salt Lake | |
City. It was an awesome experience. It's easily my favorite conference. I've | |
been to at least two dozen unique conferences. Here are some of the reasons | |
React Rally is my favorite: Tons of time to…]]></description> | |
<link>https://kentcdodds.com/blog/how-to-make-the-most-out-of-conferences</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/how-to-make-the-most-out-of-conferences</guid> | |
<pubDate>Mon, 03 Sep 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I spent the last week at <a href="https://www.reactrally.com">React Rally</a> in Salt Lake | |
City. It was an awesome experience. It&#x27;s easily my favorite conference. I&#x27;ve | |
been to at least two dozen unique conferences. Here are some of the reasons | |
React Rally is my favorite:</p><ul><li>Tons of time to meet other people</li><li>The community Rocks</li><li>Focus on meeting people and interaction</li><li>Single track</li><li>Less than 750 attendees (~550 I think)</li><li>Lunch is &quot;on your own&quot; (they give you a gift-card and you pick the local | |
restaurants)</li><li>Less alcohol (none provided in this case), more board games, yummy desserts, | |
and karaoke</li><li>Top-notch speakers, new speakers, and diverse people and topics (as diverse of | |
topics as you can get within the React ecosystem)</li><li>Organizers are just super people</li></ul><p>Things that could have made it better:</p><ul><li>Improved diversity in attendees. It&#x27;s better than some conferences, but not as | |
diverse as others</li><li>The hotel venue is a pretty convenient location, but hotels in general are a | |
little dull</li><li>Put twitter/github avatars on attendee&#x27;s badges. It will allow connections to | |
happen much more easily!</li><li>Pre-conference party would be great. Strange Loop has a very unique and | |
amazing pre-conference party/venue. It allowed me to meet a bunch of people I | |
was able to greet throughout the rest of the conference.</li><li>Speaking toward the end of the conference makes the whole experience | |
sub-optimal. But someone&#x27;s gotta do it so I was happy to take one for the team | |
😅</li></ul><p>So there&#x27;s a little feedback from my perspective for the organizers and people | |
evaluating the conference. Now, let me turn my attention to what I believe you | |
can do to get the most out of your conference experience.</p><h3>Pay attention to the talks</h3><p>This seems pretty obvious, but I definitely fall into this trap myself on | |
occasion. Sometimes you can&#x27;t get away from work, but if you&#x27;re scrolling | |
through Twitter during a talk, you&#x27;re missing out on the learning you could be | |
experiencing. Sure, you can watch the talk later, but can you watch it before | |
you meet the speaker in the hallway when it&#x27;d be the perfect time to ask them | |
the question you&#x27;ll have when you <em>actually</em> experience the talk?</p><p>Pay attention to the talks, and you&#x27;ll have common ground with anyone with whom | |
you&#x27;d like to discuss particulars and nuances of the subject, including the | |
speaker themselves. Should you take notes? That&#x27;s up to you (I don&#x27;t normally | |
take notes personally). A successful talk requires a speaker and an audience. | |
<a href="https://education.byu.edu/magazine/winter2016/act_well_thy_part">What-E&#x27;er Thou Art, Act Well Thy Part.</a></p><h3>Attend talks you think you already know all about</h3><p>You&#x27;ll be surprised what you can learn about the things you assume you already | |
know very well. Beyond that, if you really do already know the content, watching | |
someone else present it can help you learn new ways to teach the information, | |
give you a new perspective you hadn&#x27;t before considered, or potentially show you | |
a different understanding or a misunderstanding in the community about that | |
subject. Whatever the case, you will be better having listened and attended.</p><h3>Take advantage of the hallway track</h3><p>The &quot;hallway track&quot; at a conference is just a term that&#x27;s been given to the | |
impromptu conversations that naturally strike up during | |
breaks/lunch/parties/etc. Take advantage of that time. You and many others there | |
spent a lot of money and time to be there. Look for opportunities to make new | |
connections and exchange contact info (or Twitter follows). | |
<a href="https://github.com/kentcdodds/ama/issues/47">I got my current job at PayPal</a> | |
because I met a few people during the breaks at React Rally a few years ago and | |
connected with them on Twitter. You can also learn a lot. The speakers are not | |
the only experts attending the conference. Ask people about their work. Leave | |
room in your circle to be joined by others. Be as inviting as possible. You | |
never know who might be your new best friend, your next mentee, or your future | |
co-founder.</p><h3>Take breaks; take care of yourself.</h3><p>Conferences (especially of the multi-day variety) are exhausting. Drink plenty | |
of water and eat well to give you the energy that your body and brain need. When | |
you wake up in the morning, do some stretches (and air squats). And when you&#x27;re | |
feeling your body protest, <em>listen to your body</em> and find somewhere to sit, | |
disengage and disconnect. Meditate, pray, whatever. You&#x27;ll be glad that you took | |
that time to take care of yourself. This is pretty good advice for daily living | |
I think :)</p><h3>Speak</h3><p>This is a subject unto itself (I actually talked about it in last week&#x27;s | |
newsletter | |
<a href="https://kentcdodds.com/blog/why-and-how-i-started-public-speaking">&quot;Why and How I started public speaking&quot;</a>), | |
but I would encourage you to get involved in speaking at conferences. This does | |
add a whole new layer of stress to your experience, but there are a great deal | |
of benefits as well. I definitely encourage speaking as a way to get to attend | |
conferences you cannot otherwise afford as well!</p><p>When I speak, I often need some time to recharge. Definitely take care of | |
yourself. Once you&#x27;ve taken care of yourself, remember that other people may | |
want to talk with you about the content of your talk. Give them a chance to chat | |
with you. It&#x27;s a lot of fun :)</p><h3>Conclusion</h3><p>Most conferences make talk recordings available after the conference, so the | |
focus of my conference going is building relationships and meeting people. | |
Attending the talks help give you talking points and can introduce you to new | |
concepts that you may not have thought to watch after the fact anyway. Good luck | |
making the most of the conferences you&#x27;re able to attend! It&#x27;s truly a privilege | |
to go.</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://github.com/bradlc/tinker.macro">tinker.macro</a> by | |
<a href="https://twitter.com/bradlc">Brad Cornes</a> is an amazing PHP in JS macros for | |
babel-plugin-macros. How cool!?</li><li><a href="https://philipwalton.com/articles/deploying-es2015-code-in-production-today">Deploying ES2015 code in Production Today</a> — A | |
remarkably simple method for deploying ES5 and ES2015 code to browsers based | |
on whether they support <code>&lt;script type=&quot;module&quot;&gt;</code>. See also: | |
<a href="https://bundle.sh">bundle.sh</a>.</li><li><a href="https://github.com/johnlindquist/next-mdx-blog"><code>next-mdx-blog</code></a> - an example | |
by <a href="https://twitter.com/johnlindquist">John Lindquist</a>. Uses | |
<a href="https://github.com/kentcdodds/babel-plugin-preval"><code>preval</code></a> to run mdx. | |
Pretty rad TBH :)</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/how-to-make-the-most-out-of-conferences">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Why and How I started public speaking]]></title> | |
<description><![CDATA[When I was still in school in the Information Systems program at | |
BYU , I discovered and started listening to | |
JavaScript Jabber . At the time, | |
Jamison Dance was on the show and also the | |
organizer of the local JavaScript meetup. I remember him…]]></description> | |
<link>https://kentcdodds.com/blog/why-and-how-i-started-public-speaking</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/why-and-how-i-started-public-speaking</guid> | |
<pubDate>Mon, 27 Aug 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/dd302ef952fcbe9a59fdc44bd5459fbf/a296c/0.jpg" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:75%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Kent C. Dodds in front of an audience at a meetup" title="Kent C. Dodds in front of an audience at a meetup" src="https://kentcdodds.com/static/dd302ef952fcbe9a59fdc44bd5459fbf/a296c/0.jpg" srcSet="https://kentcdodds.com/static/dd302ef952fcbe9a59fdc44bd5459fbf/d2274/0.jpg 259w,https://kentcdodds.com/static/dd302ef952fcbe9a59fdc44bd5459fbf/34151/0.jpg 518w,https://kentcdodds.com/static/dd302ef952fcbe9a59fdc44bd5459fbf/a296c/0.jpg 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>When I was still in school in the Information Systems program at | |
<a href="https://www.byu.edu">BYU</a>, I discovered and started listening to | |
<a href="https://devchat.tv/js-jabber">JavaScript Jabber</a>. At the time, | |
<a href="https://twitter.com/jergason">Jamison Dance</a> was on the show and also the | |
organizer of the local JavaScript meetup. I remember him mentioning the meetup | |
on the show and thought it&#x27;d be really cool to attend and learn from others | |
(which it is!).</p><p>So I looked up the | |
<a href="https://www.meetup.com/UtahJS-Orem-Meetup/events/156148202">meetup page</a> he | |
mentioned and noticed that there was an opening for a speaker. I had just gotten | |
back from the first ng-conf (2014) and thought that whole public speaking thing | |
looked like a lot of fun. I also had been working on a personal open source | |
project on the side called <a href="https://github.com/kentcdodds/genie">geniejs</a> (which | |
was part of how I eventually got | |
<a href="https://github.com/kentcdodds/ama/issues/1">my first job</a>) and thought if they | |
needed a speaker I could talk about genie! So I offered to fill their slot:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/a8df5037617e142c582c15c08fe2a35f/8ff1e/1.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:136.875%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="a comment thread showing a request to speak at a meetup" title="a comment thread showing a request to speak at a meetup" src="https://kentcdodds.com/static/a8df5037617e142c582c15c08fe2a35f/8ff1e/1.png" srcSet="https://kentcdodds.com/static/a8df5037617e142c582c15c08fe2a35f/f4a45/1.png 259w,https://kentcdodds.com/static/a8df5037617e142c582c15c08fe2a35f/ef0f6/1.png 518w,https://kentcdodds.com/static/a8df5037617e142c582c15c08fe2a35f/8ff1e/1.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>And just like that, the first meetup I attended I was a speaker. Turns out | |
meetup organizers are often willing to give a speaking slot to anyone who so | |
much as sneezes in their direction because speakers are surprisingly hard to | |
come by on a monthly basis 😅</p><p>So there&#x27;s the history of how I got started with public speaking in tech.</p><h4><strong>So why did I do it?</strong></h4><p>I can think of two reasons:</p><ul><li>I thought my library was cool and really wanted people to start using it.</li><li>I thought it would be fun.</li></ul><p>I know a lot of people think that public speaking is worse than death. <em>How on | |
earth could I think it&#x27;s fun?</em> Well, if you don&#x27;t mind, let me give you a little | |
context into my childhood...</p><p>Ever since I was a little child, I&#x27;ve been speaking in front of groups of | |
people. As a child, I spoke in <a href="https://www.mormon.org">my Church&#x27;s</a> primary | |
program (in front of a handful of adult instructors and a few dozen of my peers) | |
a dozen times or so. When I was a teenager, I had a speech class in school, | |
started giving short talks in | |
<a href="https://www.mormonnewsroom.org/topic/seminary">my seminary classes</a>, and gave 5 | |
minute talks in front of my entire church congregation (a | |
<a href="https://www.mormonnewsroom.org/article/ward">&quot;Ward&quot;</a>consisting of ~200 people). | |
By the time I moved out of my home at 18, I had probably given 40 talks of | |
various lengths and on various subjects (though most were religious in nature). | |
During that time I also gave ~15 minute lessons to my family in | |
<a href="https://www.lds.org/topics/family-home-evening/purpose">family home evenings</a>, | |
and led lessons in my church classes as well.</p><p>In Junior High and High School, I was also a stage performer. I | |
<a href="https://youtu.be/agdbnzQlMFE">played the piano</a> (yes, I&#x27;m hitting my head and | |
forearm on the piano at ~2 minutes), | |
<a href="https://youtu.be/F5_QmWwIF3c">sang in choirs</a>, and I was in | |
<a href="https://youtu.be/5_sRK8UUc3k">my High School&#x27;s Show Choir &quot;Jive!&quot;</a> (yep, that&#x27;s | |
me in the green doing a backflip at ~6:40). After leaving home and spending a | |
semester at BYU, I left on | |
<a href="https://www.mormonnewsroom.org/topic/missionary-program">a mission for my church</a> | |
and had even more opportunities to speak in front of people (both church | |
congregations as well as teach lessons to people in their homes).</p><h4><strong>So why did I think it&#x27;d be fun?</strong></h4><p>I guess it&#x27;s because I was privileged to grow up with opportunities that allowed | |
the fears surrounding public speaking to fall away and all that&#x27;s left is the | |
pure joy of sharing experiences and connecting with people in a big way.</p><p>I&#x27;m mentioning all of this in part for me to walk down memory lane, but also to | |
illustrate that growing up, I spent a LOT of time in front of people. This will | |
hopefully give you more context into the why and how I started speaking publicly | |
at tech meetups and conferences, and hopefully will give you some ideas of what | |
you can start doing today if that&#x27;s something you want to do more of as well.</p><p>So that&#x27;s why and how I started public speaking. From there it was pretty | |
natural to transition to speaking at conferences. I wanted to speak at | |
conferences because, as I said, I&#x27;d just gotten back from my first ever tech | |
conference (ng-conf) and it was awesome. I wanted to go to more, but my company | |
wouldn&#x27;t pay for all the conferences I wanted to attend, so I needed to find | |
some other way to afford going. So my motivation to speak at conferences was | |
primarily to be able to attend.</p><p>Conference speakers normally have all expenses paid (as they should), so I | |
started searching around for open conference CFPs (Call For Proposals) (I used | |
<a href="https://twitter.com/lanyrd">Lanyrd</a>, but I think | |
<a href="https://confs.tech">confs.tech</a> is probably better these days. I&#x27;m sure there | |
are more... Google around 😉). I wrote a dozen or so proposals about subjects | |
that I knew I could write and deliver a fun and informative talk from. I | |
submitted them to every relevant conference I could. One conference I was | |
accepted to was Jfokus which is in Stockholm, Sweden (a fact that I realized | |
only <em>after</em> I&#x27;d been accepted 😅). They invited me to give a workshop as well | |
and that&#x27;s how I got my first paid workshop gig. (Another story is the fact that | |
speaking at a meetup was how I got my gig at <a href="http://egghead.io">egghead.io</a> as | |
well).</p><p>I was able to attend a ton of conferences. Too many actually. As awesome as it | |
was, it started to negatively impact my family and work. So I&#x27;ve pulled back | |
considerably, and now I have to be a lot more picky about which conferences I&#x27;ll | |
submit to.</p><h3>Conclusion</h3><p>So why do I speak at meetups and conferences now?</p><ul><li>The joy of sharing knowledge and connecting with an audience</li><li>The ability to travel and see new places</li><li>Become a recognized name in the industry to (hopefully) help secure my | |
family&#x27;s financial stability</li><li>Promote the practices and technologies that I enjoy in an effort to increase | |
the community around them</li><li>To <a href="https://kentcdodds.com/blog/solidifying-what-you-learn">solidify what I&#x27;m learning</a></li></ul><p>How did I get into public speaking?</p><ul><li><strong>I spent a TON of time in front of people.</strong> Early in childhood, these (large | |
and small) opportunities came upon me thanks to others in my life. Now I | |
actively search for these opportunities (both large and small).</li><li><strong>I wasn&#x27;t afraid of rejection.</strong> Just ask, and keep asking. Early on, I had | |
about a %5 acceptance rate for my conference proposals (though probably a 95% | |
acceptance rate for my meetup &quot;proposals&quot; 😅).</li></ul><p>I really hope this is helpful to you. I definitely still get nervous before | |
speaking, just like I was always nervous before performing in stage or speaking | |
in church as a kid. That never goes away. But the more practice you get, the | |
more able you will be to take that nervous energy and turn it into positive | |
energy to get the audience engaged which will help you connect with them better. | |
Keep practicing! Good luck :D</p><p><strong>Learn more about &quot;self improvement&quot; from me</strong>:</p><ul><li><a href="https://youtu.be/-qPh6I2hfjw?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Zero to 60 in Software Development: How to Jumpstart Your Career</a> — A | |
talk I gave a few years ago that&#x27;s related and has some similar stories to | |
this post.</li></ul><blockquote><p><em>Folks have been telling me I should make a course about (the good side of) | |
personal brand marketing / self-promotion. Let me know if you&#x27;d be interested | |
in this.</em></p></blockquote><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://hackernoon.com/how-and-why-to-speak-at-tech-conferences-1d50a3f548e0">How and Why to Speak at Tech Conferences</a> | |
by <a href="https://twitter.com/nickheiner">Nick Heiner</a> — A great blog post that goes | |
deeper on this subject.</li><li><a href="http://testandcode.com/45">Test and Code Episode 45: David Heinemeier Hansson — Software Development and Testing, TDD, and exploratory QA</a>- | |
A refreshingly practical view of testing software from the creator of Ruby on | |
Rails.</li><li><a href="http://kcd.im/devtips">DevTips</a> — If you&#x27;re not reviewing these on a regular | |
basis, I suggest you do. I put out some pretty cool things on this playlist. | |
You could make it easy on yourself and | |
<a href="http://kcd.im/youtube">subscribe to my youtube channel</a></li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/why-and-how-i-started-public-speaking">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[mdx-deck: slide decks powered by markdown and react]]></title> | |
<description><![CDATA[I've been giving presentations for years. I like many others started with | |
PowerPoint because "that's how you make presentations." I moved on from that to | |
Prezi when I was in college and I wowed all the crowds. I | |
moved on from that because it felt…]]></description> | |
<link>https://kentcdodds.com/blog/mdx-deck-slide-decks-powered-by-markdown-and-react</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/mdx-deck-slide-decks-powered-by-markdown-and-react</guid> | |
<pubDate>Mon, 20 Aug 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I&#x27;ve been giving presentations for years. I like many others started with | |
PowerPoint because &quot;that&#x27;s how you make presentations.&quot; I moved on from that to | |
<a href="https://prezi.com">Prezi</a> when I was in college and I wowed all the crowds. I | |
moved on from that because it felt too gimmicky for the kinds of presentations I | |
was making. I tried Google Slides and that was cool because it&#x27;s web-tech, but | |
was a little limited and didn&#x27;t look all that nice. Eventually I landed at | |
<a href="https://slides.com">slides.com</a>. I&#x27;ve been with slides for pretty much my | |
entire software development presentation career. You&#x27;ll find pretty much 100% of | |
the public presentations I&#x27;ve made on | |
<a href="https://slides.com/kentcdodds">my slides page</a> (including | |
<a href="https://slides.com/kentcdodds/genie">my first meetup talk</a>).</p><p>I&#x27;ve been pretty happy with slides because it&#x27;s really easy to create | |
presentations and I&#x27;ve never been one to spend a ton of time on my slides. I | |
just want to make them quickly and focus on practicing my presentation to | |
communicate effectively what I want. But it definitely has some shortcomings and | |
limitations, and there are some things about the WYSIWYG interface that really | |
bug me. So I&#x27;ve always been on the lookout for a better experience creating | |
slides. (Now&#x27;s as good a time as any to admit that I&#x27;ve never used Keynote. But | |
I didn&#x27;t want to pay for it and I don&#x27;t think that I&#x27;d be willing to spend the | |
time working on the slides to make it any better than slides anyway).</p><p>Probably the biggest example of the limitations of slides that really bothers me | |
is the difficulty of including interactive elements on the page. I always admire | |
people who&#x27;s slides are made with HTML, CSS, and JS because they can just add | |
their interactive demos directly to their slides which increases &quot;the wow | |
factor&quot; in addition to being generally more engaging. For a specific example, | |
<a href="https://slides.com/kentcdodds/simply-react">my slides</a> for my &quot;Simply React&quot; | |
keynote at ChainReact had several demos that were recorded video which is not | |
awesome, but I also had an issue where I couldn&#x27;t replay the videos | |
(<a href="https://youtu.be/M9X2qGddHkU?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf&amp;t=4m04s">watch here</a>). | |
So the demos kinda fell flat a bit.</p><p>When master <a href="https://twitter.com/ken_wheeler">Ken Wheeler</a> announced | |
<a href="https://github.com/FormidableLabs/spectacle">spectacle</a> I was super excited! It | |
is so awesome! I never got into it though because I&#x27;m just too lazy and wasn&#x27;t | |
willing to take the time to make slides out of React code and customize it to | |
what I want it to be. So though I&#x27;ve tried it a few times, it never really took | |
off for me.</p><h3>Enter MDX</h3><p>A few months ago <a href="https://twitter.com/4lpine">John Otander</a> released the initial | |
version of a new tool (and | |
<a href="https://github.com/mdx-js/specification">specification</a>) called | |
<a href="https://github.com/mdx-js/mdx">MDX</a>. Months later | |
<a href="https://twitter.com/timneutkens">Tim Neutkens</a> | |
<a href="https://youtu.be/yqACl3tRHNI?t=10m">announced MDX during the Zeit Day 2018 Keynote</a> | |
and the world&#x27;s collective minds were blown | |
(<a href="https://twitter.com/ryanflorence/status/1024522677262794752">for example</a>).</p><p>Here&#x27;s a quick example of what&#x27;s possible with MDX:</p><pre><code class="language-md">import InteractiveGraph from &#x27;./my-d3-graph&#x27; | |
# Checkout this cool graph! | |
&gt; This is markdown, for real | |
&lt;InteractiveGraph /&gt; | |
**That&#x27;s right!** We&#x27;re rendering a **React Component** in Markdown! | |
</code></pre><p>There&#x27;s a bunch that&#x27;s awesome from this. I&#x27;ve been wanting something like this | |
for quite some time! Back when I was working on the website for | |
<a href="https://github.com/paypal/glamorous">glamorous</a> | |
(<a href="https://glamorous.rocks">glamorous.rocks</a>), I wanted to make all the docs in | |
markdown to make it easier to internationalize, but I also wanted interactivity | |
to be possible, so I came up with | |
<a href="https://github.com/kentcdodds/glamorous-website/blob/master/other/CONTRIBUTING_DOCUMENTATION.md#important-markdown-notes">a super weird syntax</a> | |
to make this possible. It&#x27;s pretty cool, and actually works similar to MDX at a | |
fundamental level (uses | |
<a href="https://github.com/kentcdodds/glamorous-website/blob/b2469c1dfbfed750fc01dcbe411fec307b7ae5a8/components/interactive-markdown.js#L89-L113">a custom fancy plugin</a> | |
for <a href="https://github.com/remarkjs/remark">remark</a>), but it&#x27;s way hacky and | |
limited. This MDX thing is the REAL DEAL!</p><h3>Enter mdx-deck</h3><p>Recently, the (seriously) amazing <a href="https://twitter.com/jxnblk">Brent Jackson</a> | |
created and <a href="https://twitter.com/jxnblk/status/1023667155324346373">announced</a> | |
something absolutely amazing: <a href="https://github.com/jxnblk/mdx-deck"><strong>mdx-deck</strong></a></p><p><img src="https://camo.githubusercontent.com/c12c8d143a3509f9aa6fde5629ea0c7f78e68437/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6a786e626c6b2f6d64782d6465636b2e676966" alt="gif showing mdx-deck demo"/></p><p>It&#x27;s got the ease of slides that I love because it&#x27;s just markdown. Couldn&#x27;t be | |
much easier than that! Then, to top that off, if I want something to be | |
interactive, I can simply make that interactive thing a React component, then | |
import that directly into my slide!! How awesome is that!? Way awesome is the | |
answer!</p><p>mdx-deck has some pretty sweet features too:</p><ul><li>📝 Write presentations in markdown</li><li>⚛️ Import and use | |
<a href="https://github.com/jxnblk/mdx-deck#imports">React components</a></li><li>💅 Customizable <a href="https://github.com/jxnblk/mdx-deck#theming">themes</a> and | |
components</li><li>0️⃣ Zero-config CLI</li><li>💁 <a href="https://github.com/jxnblk/mdx-deck#presenter-mode">Presenter mode</a></li><li>📓 <a href="https://github.com/jxnblk/mdx-deck#speaker-notes">Speaker notes</a></li><li>📓 <a href="https://github.com/jxnblk/mdx-deck#exporting">Production Export</a></li><li>📜 <a href="https://github.com/jxnblk/mdx-deck#pdf-export">PDF Export</a></li></ul><p>You combine this with | |
<a href="https://www.netlify.com/docs/continuous-deployment">Netlify&#x27;s amazing GitHub Integration</a> | |
and put your slides in a GitHub project and you&#x27;re off to the races with an | |
automatically deployed slide deck!</p><h3>Conclusion</h3><p>I&#x27;m currently working on porting my slides for | |
<a href="https://github.com/kentcdodds/simply-react">&quot;Simply React&quot;</a>. You can see the | |
current state of | |
<a href="https://simply-react.netlify.com">the slides deployed on netlify here</a> (and | |
<a href="https://simply-react.netlify.com/presentation.pdf">the pdf</a>). I&#x27;m pretty jazzed | |
about the ability to have such an easy way to create presentations in the | |
browser that are easy to run locally, deployed to the web, create a PDF version, | |
<em>and</em> totally interactive. This is just terrific.</p><p>Give it a look and try it for your next presentation. I think you&#x27;ll love it. | |
Good luck!</p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/mdx-deck-slide-decks-powered-by-markdown-and-react">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[unpkg: An open source CDN for npm]]></title> | |
<description><![CDATA[A few years ago, Michael Jackson had an idea. He | |
needed an easy way to make demos for his open source projects (specifically | |
react-router ) and realized that he | |
already hosts all his projects somewhere: npm! So he could just setup a little | |
node…]]></description> | |
<link>https://kentcdodds.com/blog/unpkg-an-open-source-cdn-for-npm</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/unpkg-an-open-source-cdn-for-npm</guid> | |
<pubDate>Mon, 13 Aug 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>A few years ago, <a href="https://twitter.com/mjackson">Michael Jackson</a> had an idea. He | |
needed an easy way to make demos for his open source projects (specifically | |
<a href="https://reacttraining.com/react-router">react-router</a>) and realized that he | |
already hosts all his projects somewhere: npm! So he could just setup a little | |
node server that would act as a proxy to the files that are on npm. And here we | |
are, almost 9 BILLION downloads per month later.</p><p><a href="https://unpkg.com">unpkg</a> is an | |
<a href="https://github.com/unpkg/unpkg.com">open source</a> fast, global | |
<a href="https://en.wikipedia.org/wiki/Content_delivery_network">content delivery network</a> | |
for everything on <a href="https://www.npmjs.com">npm</a>. Use it to quickly and easily | |
load any file from any package using a URL like:</p><pre><code>unpkg.com/:package@:version/:file | |
</code></pre><p>For example, to get d3 on your page, you could add a script tag like so:</p><pre><code class="language-jsx">&lt;script src=&quot;https://unpkg.com/[email protected]/dist/d3.min.js&quot; /&gt; | |
</code></pre><p>You could also do:</p><pre><code class="language-jsx">&lt;script src=&quot;https://unpkg.com/d3&quot; /&gt; | |
</code></pre><p>Because unpkg redirects those to the above URL (in this case it&#x27;s because d3&#x27;s | |
<code>package.json</code> has a <code>unpkg</code> field to point to that file specifically). It&#x27;s | |
recommended that you specify a version though because otherwise user&#x27;s will | |
start downloading the latest version which could break your application if | |
there&#x27;s a major version bump:</p><pre><code class="language-jsx">&lt;script src=&quot;https://unpkg.com/d3@^5.5.0&quot; /&gt; | |
</code></pre><p>That&#x27;s right, a version range works in there as well. Cool right?</p><p>So why is this so cool? Whelp, we use CDNs (content delivery networks) because | |
they allow static assets like images, JavaScript, and videos to be hosted | |
physically close to end users as well as served with as fast as possible | |
technology. unpkg is sponsored by <a href="https://www.heroku.com">Heroku</a> where it is | |
hosted, but that server is only actually used 5% of the time. The real power of | |
a tool like unpkg is the fact that the files hosted at those URLs can be very | |
heavily cached (npm doesn&#x27;t allow published packages to be changed). So unpkg is | |
also sponsored by <a href="https://www.cloudflare.com">Cloudflare</a> which is an awesome | |
CDN and serves 95% of unpkg&#x27;s traffic from the cache, making unpkg extremely | |
fast.</p><p>unpkg is great for open source project demos and instructional material (I use | |
it heavily in my <a href="http://kcd.im/beginner-react">Beginner&#x27;s Guide to ReactJS</a>), | |
but it&#x27;s not well suited for mission-critical applications at scale because:</p><blockquote><p><em>unpkg is a free, best-effort service and cannot provide any uptime or support | |
guarantees.</em></p></blockquote><p>That&#x27;s why Michael recommends:</p><blockquote><p><em>if you rely on it to serve files that are crucial to your business, you | |
should probably pay for a host with well-supported infrastructure and uptime | |
guarantees.</em></p></blockquote><p>This is something that I plan on doing at PayPal eventually and I&#x27;ll tell you | |
why. Most projects at PayPal are using much of the same technology. Most are | |
using some version of react and react-dom, some are using rxjs, many are using | |
lodash. Each of these projects serves its own <code>bundle.js</code> file(s) that include | |
these dependencies. So as users navigate around PayPal they&#x27;re re-downloading | |
much of the same code just in a different form. Some companies enforce the | |
entire company use the same version of some dependencies to avoid this problem. | |
I think this comes with more problems than the solution merits</p><p>This is why I&#x27;m really interested in building a hosted version of unpkg at | |
PayPal. Doing this will allow teams to use whatever version of whatever | |
dependencies they like. If two teams happen to be using the same version of | |
React (pretty likely), then the user wont have to download that version of react | |
more than once. This compounds across the number of teams and projects PayPal | |
has. And because I also write and maintain | |
<a href="https://kentcdodds.com/blog/tools-without-config">paypal-scripts</a>, I can build-in a really nice | |
process into paypal-scripts so people can get this functionality out of the box. | |
Automatic user experience improvement! Woo!</p><h3>Conclusion</h3><p>I have a lot of things on my plate, but I&#x27;m hoping to be able to do this in the | |
next few months. I think it&#x27;ll be a real win for people using PayPal products. | |
Can&#x27;t wait to see those bundle sizes getting smaller! Good luck friends!</p><p>P.S. One other thing that I really love about unpkg is the index page for a | |
package. Simply add a <code>/</code> at the end of the URL and you&#x27;ll see an index of the | |
files as well as a version chooser which is pretty awesome: | |
<a href="https://unpkg.com/d3"><code>https://unpkg.com/d3/</code></a></p><p><strong>Learn more about JavaScript from me</strong>:</p><ul><li><a href="https://youtu.be/kTlcu16rSLc&amp;list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">More than you want to know about ES6 Modules @ Learn to Code Websites and Apps Meetup (remote)</a></li><li><a href="https://youtu.be/t3R3R7UyN2Y&amp;list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">ES6 and Beyond Workshop Part 1 at PayPal (Jan 2017)</a></li><li><a href="https://youtu.be/eOKQDh50ECU&amp;list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">ES6 and Beyond Workshop Part 2 at PayPal (March 2017)</a></li><li><a href="https://kentcdodds.com/workshops/#code-transformation-and-linting">Code Transformation and Linting</a></li><li><a href="https://kentcdodds.com/talks/#writing-custom-babel-and-eslint-plugins-with-asts">Writing custom Babel and ESLint plugins with ASTs</a></li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://gh-polls.com">gh-polls.com</a> — &quot;GH polls is a quick and effective way | |
to request feedback from community members in GitHub issues.&quot; by | |
<a href="https://twitter.com/tjholowaychuk">TJ Holowaychuk</a>.</li><li><a href="https://there.pm">There</a> — A really neat app by | |
<a href="https://twitter.com/morajabi">Mo Rajabifard</a> that&#x27;ll tell you what time it is | |
for your friends and co-workers in a nice UI.</li><li><a href="https://github.com/elbywan/wretch">wretch</a> — &quot;A tiny wrapper built around | |
fetch with an intuitive syntax. 🍬&quot; by | |
<a href="https://github.com/elbywan">Julien Elbaz</a>.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/unpkg-an-open-source-cdn-for-npm">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Polyfill as needed with polyfill-service]]></title> | |
<description><![CDATA[In last week's newsletter "What is a polyfill" , I | |
talked about a situation I came across with a white screen on IE10 (the app | |
crashed because we were missing polyfills). I explained a bit of the difference | |
between a polyfill and a code transform. I…]]></description> | |
<link>https://kentcdodds.com/blog/polyfill-as-needed-with-polyfill-service</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/polyfill-as-needed-with-polyfill-service</guid> | |
<pubDate>Mon, 06 Aug 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>In last week&#x27;s newsletter <a href="https://kentcdodds.com/blog/what-is-a-polyfill">&quot;What is a polyfill&quot;</a>, I | |
talked about a situation I came across with a white screen on IE10 (the app | |
crashed because we were missing polyfills). I explained a bit of the difference | |
between a polyfill and a code transform. I explained a few options you have at | |
your disposal to use new JavaScript features and still support older browsers. | |
In the conclusion I said this:</p><blockquote><p><em>So what did I do to fix my IE10 bug? Well, one thing that really bugs me is | |
that I have to ship all this code for polyfills to all browsers even if they</em> | |
do <em>support these features. But a few years ago I heard of</em> &gt; | |
<a href="https://polyfill.io"><em>a service</em></a> <em>that was able to ship polyfills that are | |
relevant only to the browser requesting them. I created my own endpoint that | |
uses</em> <a href="https://github.com/Financial-Times/polyfill-service"><em>the module</em></a> &gt; | |
<em>that powers that service and I&#x27;ll write about that next week!</em></p></blockquote><p>That&#x27;s today&#x27;s newsletter! I&#x27;ll explain how I created a <code>polyfill.js</code> endpoint | |
that gives back a very aggressively cached JavaScript file with the polyfills | |
that users need and no more.</p><h3>Why polyfill-service?</h3><p>With the way I have my usage of polyfill-service configured today, if I make a | |
request for <code>polyfill.js</code> using Internet Explorer 10 (the lowest version of IE | |
that we support), the response is 60.2kb! If you&#x27;re unfamiliar with the impact | |
this can make, I suggest you read | |
<a href="https://medium.com/dev-channel/the-cost-of-javascript-84009f51e99e">The Cost Of JavaScript</a> | |
by <a href="https://twitter.com/addyosmani">Addy Osmani</a> (or | |
<a href="https://youtu.be/63I-mEuSvGA">watch a talk version here</a>). To put this in terms | |
you may appreciate, this will take users in emerging markets about a full second | |
just to download, then you have to take the content they&#x27;ve downloaded and | |
parse/compile/run it which can take even longer especially for individuals using | |
lower-end phones.</p><p>The state of the art with polyfills is to include those polyfills in your | |
<code>bundle.js</code>file (in fact, lots of apps are just using all of <code>core-js</code> which is | |
<a href="https://bundlephobia.com/[email protected]">84.2 kb of minified JS</a>). This | |
means that every browser will need to download, parse, and run that JavaScript | |
regardless of what browser they&#x27;re using. But let&#x27;s take a look at | |
<a href="https://caniuse.com/usage-table">browser usage statistics</a>. Your stats may vary | |
depending on your users, but if your app is typical of the global averages, then | |
you have <em>maybe</em> 5% of your users who need more than a handful of kbs worth of | |
polyfills. Most of your users will be using modern, evergreen browsers that | |
support most of the features you&#x27;re using. So you&#x27;re making users who are | |
running modern browsers pay a &quot;tax&quot; for your site supporting those 5% of users | |
who wont/can&#x27;t upgrade.</p><p>If I run Chrome 67 on my <code>polyfill.js</code> file, it comes back | |
<a href="https://cdn.polyfill.io/v2/polyfill.min.js">basically empty</a>. By using | |
polyfill-service, only the browsers which <em>need</em> the polyfills receive them. | |
This means that they can use my app quicker and I&#x27;m not taking up some of your | |
bandwidth to download stuff you don&#x27;t need (which actually means saving people | |
actual dollars if they don&#x27;t have unlimited data).</p><p>Another aspect of using something like polyfill-service is because my polyfills | |
live in a completely different file from my <code>bundle.js</code>, I can have it cached | |
forever, so users only need to download it once and never need to download it | |
again. So even for users on bad networks, they&#x27;ll benefit from not having to | |
expend resources re-downloading a file that will never change.</p><h3>Using polyfill-service</h3><p>The <a href="https://polyfill.io">polyfill.io</a> service from Financial Times is awesome, | |
but with no SLA (service level agreement), many companies can&#x27;t rely on it for | |
mission-critical applications. Luckily, the module that powers it is completely | |
open source so you can set up your own service in-house in a pretty | |
straightforward way and that&#x27;s exactly what I did.</p><p>With the app I&#x27;m working on right now (<a href="https://paypal.me">paypal.me</a>), we have | |
a server that&#x27;s responsible for some light server-rendering for SEO purposes. | |
Basically, our server is a NodeJS server using <a href="http://krakenjs.com">KrakenJS</a> | |
(a wrapper on top of <a href="https://expressjs.com">express</a>), so I added a | |
<code>get</code>handler to the express app:</p><pre><code class="language-js">app.get(&#x27;/polyfill.js&#x27;, getBrowserPolyfill) | |
</code></pre><p>And with the <code>getBrowserPolyfill</code> is a typical express route handler:</p><pre><code class="language-js">import polyfill from &#x27;polyfill-service&#x27; | |
async function getBrowserPolyfill(req, res) { | |
const script = await polyfill.getPolyfillString({ | |
/* options */ | |
}) | |
res.set({ | |
&#x27;Content-Type&#x27;: &#x27;application/javascript;charset=utf-8&#x27;, | |
&#x27;Content-Length&#x27;: script.length, | |
}) | |
if (shouldCacheAggressively) { | |
res.setHeader(&#x27;Cache-Control&#x27;, &#x27;immutable&#x27;) | |
} | |
res.write(script) | |
res.end() | |
} | |
</code></pre><p>There&#x27;s a little bit more to it, but this is the basic idea. So let&#x27;s talk about | |
a few aspects of this solution.</p><h3>User Agent</h3><p>So the polyfill-service module needs to know what the user agent string is to | |
determine what the <code>script</code> string should be (which JavaScript polyfills to | |
include). So I pass <code>req.headers[&#x27;user-agent&#x27;]</code> as that value, though I allow | |
the <code>ua</code> query string to override this and I have a fallback to IE 9 just in | |
case. And in the case that polyfill-service encounters a user agent it doesn&#x27;t | |
recognize, I have it configured to just treat it as if it needs all the | |
polyfills (via the <code>unknown: &#x27;polyfill&#x27;</code> option).</p><h3>Features</h3><p>There are a LOT of features that polyfill-service supports out of the box. It | |
defaults to the most useful ones, but it&#x27;s a good idea to configure it. At first | |
I thought: &quot;Hey, let&#x27;s just have it support everything.&quot; But then I found out | |
that if you asked it to polyfill <em>everything</em> it could, it&#x27;ll get HUGE (mostly | |
because it actually supports <code>Intl</code> with every language pack which is kinda | |
reeeally big). So I ended up with specifying <code>es2015</code>, <code>es2016</code>, <code>es2017</code>, | |
<code>es2018</code>, and <code>default-3.6</code> as the features config. That&#x27;s working great and | |
supports everything that I care to support.</p><h3>Caching</h3><p>This one&#x27;s a bit interesting. So that <code>shouldCacheAggressively</code> is a bit | |
dangerous, so here&#x27;s what I do... Because we&#x27;re server-rendering the page, I can | |
actually generate the URL for the polyfill. It ends up looking like this (for IE | |
11):</p><pre><code>polyfill.js?v=2&amp;ua=Mozilla%2F5.0%20(Windows%20NT%2011.0%3B%20WOW64%3B%20Trident%2F7.0%3B%20rv%3A11.0)%20like%20Gecko | |
</code></pre><p>There are two query strings on there: <code>v</code> which is associated to a <code>version</code> | |
that I have hard-coded. This allows me to break the cache in the event of an | |
emergency if we need to change the config or something.</p><p>I also generate it with the <code>ua</code> which is the user agent as part of the query | |
string for the <code>polyfill.js</code> file. Remember how I mentioned earlier that I allow | |
the <code>uq</code>query string to override <code>req.headers[&#x27;user-agent&#x27;]</code>? So that&#x27;s what | |
this is doing. The reason I do this is for caching. With such a specific URL, I | |
can safely cache this forever. If the user upgrades (or downgrades!?) their | |
browser, but the cache isn&#x27;t deleted, then this URL is changed and the old | |
cached version isn&#x27;t used.</p><h3>Extras</h3><p>One <a href="https://twitter.com/kentcdodds/status/997228884864139264">&quot;fun&quot; experience</a> | |
I had while building this involved polyfill-service not playing nice with the | |
way that babel transpiles classes. Follow that twitter thread and github issues | |
linked for a &quot;fun&quot; time of your own... 😅</p><h3>Conclusion</h3><p>I&#x27;m excited about this and I&#x27;m hoping to build a more official polyfill service | |
for more of PayPal applications to use it so folks can build applications with | |
the latest JavaScript features without worrying about whether older browsers | |
natively support what they&#x27;re writing and without making users of modern | |
browsers pay a &quot;tax&quot; for users of older browsers.</p><p>Best of luck to you!</p><p>P.S. Soon after sending this newsletter, | |
<a href="https://github.com/kddeisz">Kevin Deisz</a> made | |
<a href="https://github.com/CultureHQ/polyfill-lambda">an open source AWS Lambda service</a>. | |
Pretty cool!</p><p><strong>Learn more about JavaScript from me</strong>:</p><ul><li><a href="https://youtu.be/kTlcu16rSLc?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">More than you want to know about ES6 Modules @ Learn to Code Websites and Apps Meetup (remote)</a></li><li><a href="https://youtu.be/t3R3R7UyN2Y?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">ES6 and Beyond Workshop Part 1 at PayPal (Jan 2017)</a></li><li><a href="https://youtu.be/eOKQDh50ECU?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">ES6 and Beyond Workshop Part 2 at PayPal (March 2017)</a></li><li><a href="https://kentcdodds.com/workshops/#code-transformation-and-linting">Code Transformation and Linting</a></li><li><a href="https://kentcdodds.com/talks/#writing-custom-babel-and-eslint-plugins-with-asts">Writing custom Babel and ESLint plugins with ASTs</a></li></ul><p>Also, don&#x27;t forget to subscribe to <a href="http://kcd.im/youtube">my youtube channel</a> | |
for my daily devtips, like | |
<a href="https://youtu.be/FsgGx1SMXn0?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">the one a while back where</a> | |
I demo some advanced features of destructuring!</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://github.com/davidbanham/express-async-errors"><code>express-async-errors</code></a> - | |
a nice package that allows me to use <code>async/await</code>on express route | |
handlers/middleware without worrying about rejected promises being ignored and | |
making my server hang :)</li><li><a href="https://youtu.be/2HnNo4t8534?t=541">ReactJS Denver: Confidently Testing React Apps</a> — A | |
great talk at <a href="https://www.meetup.com/ReactDenver">React Denver</a> by | |
<a href="https://twitter.com/mattparrish">Matt Parrish</a>.</li><li><a href="https://www.robinwieruch.de/javascript-fundamentals-react-requirements">React is no abstraction, React is JavaScript</a> — A | |
very interesting and important blog post from | |
<a href="https://twitter.com/rwieruch">Robin Wieruch</a> that teaches some common | |
JavaScript fundamentals that you&#x27;ll need when working with React.</li><li><a href="https://medium.com/@ow/the-surface-book-2-is-everything-the-macbook-pro-should-be-5ef560edb505">The Surface Book 2 is everything the MacBook Pro should be</a> | |
by <a href="https://twitter.com/ow">Owen Williams</a> — I&#x27;m getting more and more | |
convinced that my next computer is NOT going to be a MacBook. Here&#x27;s hoping | |
that the Surface Book can get me 32 GB of RAM before I need another computer!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/polyfill-as-needed-with-polyfill-service">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[What is a polyfill]]></title> | |
<description><![CDATA[A few weeks back I found a bug with IE where all the user saw was a blank white | |
page. If you've been around for a while in the wonderful world of the | |
client-side SPA, you'll probably know what was wrong without thinking twice. | |
That's right. It was a…]]></description> | |
<link>https://kentcdodds.com/blog/what-is-a-polyfill</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/what-is-a-polyfill</guid> | |
<pubDate>Mon, 30 Jul 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>A few weeks back I found a bug with IE where all the user saw was a blank white | |
page. If you&#x27;ve been around for a while in the wonderful world of the | |
client-side SPA, you&#x27;ll probably know what was wrong without thinking twice. | |
That&#x27;s right. It was a JavaScript error before client-side rendering happened.</p><p>Considering it the bug only rears its head in Internet Explorer, my first guess | |
is a problem with polyfills. Yep! That was it!</p><pre><code>Uncaught TypeError: contacts.includes is not a function | |
</code></pre><p>But we&#x27;re transpiling our code with Babel! Doesn&#x27;t that mean that I can use all | |
the latest and greatest JavaScript I want without having to worry about whether | |
the browser supports it? Nope! Let&#x27;s learn more...</p><h3>Polyfills vs Code Transforms</h3><p>JavaScript is constantly evolving thanks to the efforts of people in and around | |
<a href="https://github.com/tc39">the TC39</a>. Some of the evolutions rely on new syntax | |
(like | |
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">arrow functions</a>) | |
which allows me to do this:</p><pre><code class="language-js">const addOne = num =&gt; num + 1 | |
if (addOne(2) &gt; 2) { | |
console.log(&#x27;Math. Wow!&#x27;) | |
} | |
</code></pre><p>Often, we can use this syntax in our source code so long as we convert it to | |
syntax that can run in the browser (for example, by using a transpiler such as | |
<a href="https://babeljs.io">babel</a>: | |
<a href="http://babeljs.io/repl/#?babili=false&amp;browsers=ie%2010&amp;build=&amp;builtIns=false&amp;spec=false&amp;loose=false&amp;code_lz=MYewdgzgLgBAhgEwQeTAUxgXhmArgWywD4cCYBqGARgCgBLAMxgApEV1mAmAShhJ5gBvGjBihIIADZoAdJJABzZgHIAsnCgALGTADqIAO4BCZdxoBfGkA&amp;debug=false&amp;forceAllTransforms=false&amp;shippedProposals=false&amp;circleciRepo=&amp;evaluate=true&amp;fileSize=false&amp;sourceType=module&amp;lineWrap=true&amp;presets=env&amp;prettier=false&amp;targets=&amp;version=6.26.0&amp;envVersion=1.6.2">example transpiled in the browser with babel-preset-env</a>).</p><p>Some other of these new features rely on new APIs, like | |
<code>Array.prototype.includes</code> which allows me to do this:</p><pre><code class="language-js">const contacts = [&#x27;Brooke&#x27;, &#x27;Becca&#x27;, &#x27;Nathan&#x27;, &#x27;Adam&#x27;, &#x27;Michael&#x27;] | |
if (contacts.includes(&#x27;Rachel&#x27;)) { | |
console.log(&#x27;You have a Rachel!&#x27;) | |
} | |
</code></pre><p>With these, if you | |
<a href="http://babeljs.io/repl/#?babili=false&amp;browsers=ie%2010&amp;build=&amp;builtIns=false&amp;spec=false&amp;loose=false&amp;code_lz=MYewdgzgLgBKZQIbChGBeGBtA5AIQCcQQBrAUxwBoZ8zhhEqaA5RKAC0TCZwEEATRAFseAWQCWwTmQA2OALoAocQDMYACnhIUEAHTiwwGQFd-ZCOpwAlZO1k4AlA5gBvRTDjgIIGWV0yQAHNLAE0QYxhOADcyGEQYGylZAEJHRQBfRSA&amp;debug=false&amp;forceAllTransforms=false&amp;shippedProposals=false&amp;circleciRepo=&amp;evaluate=true&amp;fileSize=false&amp;sourceType=module&amp;lineWrap=true&amp;presets=env&amp;prettier=false&amp;targets=&amp;version=6.26.0&amp;envVersion=1.6.2">run them through babel&#x27;s env preset</a> | |
the <code>includes</code> function is not transpiled because it&#x27;s not a syntax issue, but a | |
built-in API one and babel&#x27;s env preset only includes transforms for syntax | |
transformations. You <em>could</em> | |
<a href="https://kentcdodds.com/talks/#writing-custom-babel-and-eslint-plugins-with-asts">write your own babel plugin</a> | |
(<a href="https://astexplorer.net/#/gist/538b72e2af148a14d7c0f5824b431cd6/47a57f42697199d6cfa1d4b1027951ef170a980e">like this</a>) | |
to transform the code, but for <em>some</em> APIs it just wouldn&#x27;t be practical because | |
the transformed version would be significantly complex.</p><p>A polyfill is code which will make the currently running JavaScript environment | |
support features which it does not. For example, a (imperfect) polyfill for | |
<code>includes</code> might look something like this | |
(<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes">refer to MDN</a> | |
for a real polyfill):</p><pre><code class="language-js">if (!Array.prototype.includes) { | |
Array.prototype.includes = function includes(searchElement) { | |
return this.indexOf(searchElement) !== -1 | |
} | |
} | |
</code></pre><p>The <code>if</code> statement is there to make this a &quot;gated&quot; polyfill. That means that if | |
the functionality already exists, the polyfill code will <em>not</em> override the | |
pre-existing behavior. You may consider this to be desirable, but it&#x27;s actually | |
the reason that <code>includes</code>is not called <code>contains</code> on <code>String.prototype</code> (TL;DR: | |
some versions of mootools implemented <code>contains</code> in a gated fashion but the | |
implementation is different from how <code>includes</code> works so the TC39 had to change | |
the name to not break tons of websites).</p><p>The part that assigns <code>Array.prototype.includes</code> to a function is called | |
&quot;monkey-patching&quot; 🐒 By applying this to the prototype, we&#x27;re adding <code>includes</code> | |
support to all arrays in the app | |
(<a href="https://github.com/getify/You-Dont-Know-JS/blob/f0d591b6502c080b92e18fc470432af8144db610/this%20%26%20object%20prototypes/ch5.md">learn more about prototypes here</a>). | |
Effectively, the polyfill&#x27;s job is to make it so I can use the JavaScript | |
feature without worrying about whether it&#x27;s supported by the environment in | |
which my code is running (like IE 10 for example).</p><h3>Where to get transforms and polyfills</h3><p>With syntax transforms, I recommend | |
<a href="https://babeljs.io/docs/en/next/babel-preset-env.html">babel-preset-env</a>. It&#x27;s | |
actually fairly straightforward. For polyfills, the most popular one is | |
<a href="https://www.npmjs.com/package/core-js"><code>core-js</code></a>. You might also look at | |
<a href="https://babeljs.io/docs/en/next/babel-polyfill.html"><code>babel-polyfill</code></a> which | |
uses <code>core-js</code> and a custom <code>regenerator runtime</code> to support generators and | |
async/await the way that babel transpiles it. Polyfills are sometimes referred | |
to as &quot;shims&quot; and you may be interested in the | |
<a href="https://github.com/airbnb/js-shims"><code>js-shims</code></a> by airbnb (which I&#x27;ve been told | |
are more spec-complient than <code>core-js</code>).</p><h3>Conclusion</h3><p>So what did I do to fix my IE10 bug? Well, one thing that really bugs me is that | |
I have to ship all this code for polyfills to all browsers even if they <em>do</em> | |
support these features. But a few years ago I heard of | |
<a href="https://polyfill.io">a service</a> that was able to ship polyfills that are | |
relevant only to the browser requesting them. I created my own endpoint that | |
uses <a href="https://github.com/Financial-Times/polyfill-service">the module</a> that | |
powers that service and I&#x27;ll write about that next week!</p><p>I hope this is helpful! Good luck!</p><p>P.S. You may have heard of something called a &quot;ponyfill.&quot; Ponyfills are similar | |
to polyfills except they don&#x27;t monkey-patch, instead they&#x27;re just the function | |
by itself and allow you to call them directly. | |
<a href="https://github.com/sindresorhus/ponyfill">Learn more about ponyfills</a>. In | |
general, I&#x27;m more in favor of ponyfills, though you just can&#x27;t get away from | |
polyfills completely because often your dependencies are relying on built-ins | |
that your browsers don&#x27;t support.</p><p><strong>Learn more about React from me</strong>:</p><ul><li><a href="http://kcd.im/beginner-react">The Beginner&#x27;s Guide to React</a></li><li><a href="http://kcd.im/advanced-react">Advanced React Component Patterns</a> (also on | |
<a href="https://frontendmasters.com/courses/advanced-react-patterns">Frontend Masters</a>).</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://github.com/testdouble/cypress-capybara">cypress-capybara</a> — If you&#x27;ve | |
used <a href="https://github.com/teamcapybara/capybara">capybara</a> before you&#x27;ll | |
probably love this util from <a href="https://twitter.com/searls">Justin Searls</a>. If | |
you like this, then you&#x27;ll probably love | |
<a href="https://github.com/testing-library/cypress-testing-library">cypress-testing-library</a>. | |
In any case, | |
<a href="https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change">don&#x27;t reuse your CSS selectors as test selectors</a>!</li><li><a href="https://github.com/joshwcomeau/guppy">guppy</a> — 🐠A friendly application | |
manager and task runner for React.js by | |
<a href="https://twitter.com/joshwcomeau">Josh Comeau</a>.</li><li><a href="https://twitter.com/CompuIves/status/1018871036719325184">Themes Support in Codesandbox</a> | |
🤩 — <a href="http://kcd.im/mft">I set mine to Night Owl and Dank.sh</a> so fast you | |
wouldn&#x27;t believe it. That <a href="https://twitter.com/CompuIves">Ives van Hoorne</a> has | |
gone and done some amazing work again!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/what-is-a-polyfill">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Why I Never Use Shallow Rendering]]></title> | |
<description><![CDATA[I remember a few years ago when I got started with React I decided I needed to | |
figure out how to test React components. I tried | |
shallow from enzyme and | |
immediately decided that I would never use it to test my React components. I've | |
expressed this…]]></description> | |
<link>https://kentcdodds.com/blog/why-i-never-use-shallow-rendering</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/why-i-never-use-shallow-rendering</guid> | |
<pubDate>Mon, 23 Jul 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I remember a few years ago when I got started with React I decided I needed to | |
figure out how to test React components. I tried | |
<a href="http://airbnb.io/enzyme/docs/api/shallow.html"><code>shallow</code></a> from enzyme and | |
immediately decided that I would never use it to test my React components. I&#x27;ve | |
expressed this feeling on many occasions and get asked on a regular basis why I | |
feel the way I do about <code>shallow</code> rendering and why | |
<a href="https://github.com/testing-library/react-testing-library"><code>react-testing-library</code></a> | |
will never support <code>shallow</code> rendering.</p><p>So finally I&#x27;m coming out with it and explaining why I never use shallow | |
rendering and why I think nobody else should either. Here&#x27;s my main assertion:</p><blockquote><p><strong><em>With shallow rendering, I can refactor my component&#x27;s implementation and my | |
tests break. With shallow rendering, I can break my application and my tests | |
say everything&#x27;s still working.</em></strong></p></blockquote><p>This is highly concerning to me because not only does it make testing | |
frustrating, but it also lulls you into a false sense of security. <strong>The reason | |
I write tests is to be confident that my application works and there are far | |
better ways to do that than shallow rendering.</strong></p><h3>What even is shallow rendering?</h3><p>For the purposes of this article, let&#x27;s use this example as our subject under | |
test:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import {CSSTransition} from &#x27;react-transition-group&#x27; | |
function Fade({children, ...props}) { | |
return ( | |
&lt;CSSTransition {...props} timeout={1000} className=&quot;fade&quot;&gt; | |
{children} | |
&lt;/CSSTransition&gt; | |
) | |
} | |
class HiddenMessage extends React.Component { | |
static defaultProps = {initialShow: false} | |
state = {show: this.props.initialShow} | |
toggle = () =&gt; { | |
this.setState(({show}) =&gt; ({show: !show})) | |
} | |
render() { | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={this.toggle}&gt;Toggle&lt;/button&gt; | |
&lt;Fade in={this.state.show}&gt; | |
&lt;div&gt;Hello world&lt;/div&gt; | |
&lt;/Fade&gt; | |
&lt;/div&gt; | |
) | |
} | |
} | |
export {HiddenMessage} | |
</code></pre><p>Here&#x27;s an example of a test that uses shallow rendering with enzyme:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
import Enzyme, {shallow} from &#x27;enzyme&#x27; | |
import Adapter from &#x27;enzyme-adapter-react-16&#x27; | |
import {HiddenMessage} from &#x27;../hidden-message&#x27; | |
Enzyme.configure({adapter: new Adapter()}) | |
test(&#x27;shallow&#x27;, () =&gt; { | |
const wrapper = shallow(&lt;HiddenMessage initialShow={true} /&gt;) | |
expect(wrapper.find(&#x27;Fade&#x27;).props()).toEqual({ | |
in: true, | |
children: &lt;div&gt;Hello world&lt;/div&gt;, | |
}) | |
wrapper.find(&#x27;button&#x27;).simulate(&#x27;click&#x27;) | |
expect(wrapper.find(&#x27;Fade&#x27;).props()).toEqual({ | |
in: false, | |
children: &lt;div&gt;Hello world&lt;/div&gt;, | |
}) | |
}) | |
</code></pre><p>To understand shallow rendering, let&#x27;s add a <code>console.log(wrapper.debug())</code> | |
which will log out the structure of what enzyme has rendered for us:</p><pre><code class="language-jsx">&lt;div&gt; | |
&lt;button onClick={[Function]}&gt;Toggle&lt;/button&gt; | |
&lt;Fade in={true}&gt; | |
&lt;div&gt;Hello world&lt;/div&gt; | |
&lt;/Fade&gt; | |
&lt;/div&gt; | |
</code></pre><p>You&#x27;ll notice that it&#x27;s not actually showing the <code>CSSTransition</code> which is what | |
<code>Fade</code> is rendering. This is because instead of actually rendering components | |
and calling into the <code>Fade</code> component, shallow just looks at the props that | |
would be applied to the React elements created by the component you&#x27;re shallowly | |
rendering. In fact, if I were to take the <code>render</code> method of the <code>HiddenMessage</code> | |
component and <code>console.log</code> what it&#x27;s returning, I&#x27;d get something that looks a | |
bit like this:</p><pre><code class="language-js">{ | |
&quot;type&quot;: &quot;div&quot;, | |
&quot;props&quot;: { | |
&quot;children&quot;: [ | |
{ | |
&quot;type&quot;: &quot;button&quot;, | |
&quot;props&quot;: { | |
&quot;onClick&quot;: [Function], | |
&quot;children&quot;: &quot;Toggle&quot; | |
} | |
}, | |
{ | |
&quot;type&quot;: [Function: Fade], | |
&quot;props&quot;: { | |
&quot;in&quot;: true, | |
&quot;children&quot;: { | |
&quot;type&quot;: &quot;div&quot;, | |
&quot;props&quot;: { | |
&quot;children&quot;: &quot;Hello world&quot; | |
} | |
} | |
} | |
} | |
] | |
} | |
} | |
</code></pre><p>Look familiar? So all shallow rendering is doing is taking the result of the | |
given component&#x27;s <code>render</code> method (which will be a React element (read | |
<a href="https://kentcdodds.com/blog/what-is-jsx">What is JSX?</a>)) and giving us a <code>wrapper</code> object with some | |
utilities for traversing this JavaScript object. This means it doesn&#x27;t run | |
lifecycle methods (because we just have the React elements to deal with), it | |
doesn&#x27;t allow you to actually interact with DOM elements (because nothing&#x27;s | |
actually rendered), and it doesn&#x27;t actually attempt to get the react elements | |
that are returned by your custom components (like our <code>Fade</code> component).</p><h3>Why people use shallow rendering</h3><p>When I determined early on to never use shallow rendering, it was because I knew | |
that there were better ways to get at the things that shallow rendering makes | |
easy without making the trade-offs shallow rendering forces you to make. | |
<a href="https://twitter.com/kentcdodds/status/1016084196421296133">I recently asked folks</a> | |
to tell me why they use shallow rendering. Here are a few of the things that | |
shallow rendering makes easier:</p><ol><li><a href="https://twitter.com/Raed667/status/1016095856481701888">... for calling methods in React components</a></li><li><a href="https://twitter.com/MattTrifilo/status/1016101577667403778">... it seems like a waste to render all of the children of each component under test, for every test, hundreds/thousands of times...</a></li><li><a href="https://twitter.com/janhoogeveen/status/1016207627859251200">For actual unit testing. Testing composed components introduces new dependencies that might trigger an error while the unit itself might still work as intended.</a></li></ol><p>There were more responses, but these sum up the main arguments for using shallow | |
rendering. Let&#x27;s address each of these:</p><h3>Calling methods in react components</h3><p>Have you ever seen or written a test that looks like this?</p><pre><code class="language-jsx">test(&#x27;toggle toggles the state of show&#x27;, () =&gt; { | |
const wrapper = shallow(&lt;HiddenMessage initialShow={true} /&gt;) | |
expect(wrapper.state().show).toBe(true) // initialized properly | |
wrapper.instance().toggle() | |
wrapper.update() | |
expect(wrapper.state().show).toBe(false) // toggled | |
}) | |
</code></pre><p>This is a great reason to use shallow rendering, but it&#x27;s a really poor testing | |
practice. There are two really important things that I try to consider when | |
testing:</p><ol><li>Will this test break when there&#x27;s a mistake that would break the component | |
in production?</li><li>Will this test continue to work when there&#x27;s a fully backward compatible | |
refactor of the component?</li></ol><p>This kind of test fails both of those considerations:</p><ol><li>I could mistakenly set <code>onClick</code> of the <code>button</code> to <code>this.tgogle</code> instead of | |
<code>this.toggle</code>. My test continues to work, but my component is broken.</li><li>I could rename <code>toggle</code> to <code>handleButtonClick</code> (and update the corresponding | |
<code>onClick</code> reference). My test breaks despite this being a refactor.</li></ol><p>The reason this kind of test fails those considerations is because it&#x27;s testing | |
irrelevant implementation details. The user doesn&#x27;t care one bit what things are | |
called. In fact, that test doesn&#x27;t even verify that the message is hidden | |
properly when the <code>show</code> state is <code>false</code> or shown when the <code>show</code> state is | |
<code>true</code>. So not only does the test not do a great job keeping us safe from | |
breakages, it&#x27;s also flakey and doesn&#x27;t actually test the reason the component | |
exists in the first place.</p><p>In summary, if your test uses <code>instance()</code> or <code>state()</code>, know that you&#x27;re | |
testing things that the user couldn&#x27;t possibly know about or even care about, | |
which will take your tests further from giving you confidence that things will | |
work when your user uses them.</p><h3>... it seems like a waste ...</h3><p>There&#x27;s no getting around the fact that shallow rendering is faster than any | |
other form of testing react components. It&#x27;s certainly way faster than mounting | |
a react component. But we&#x27;re talking a handful of milliseconds here. Yes, it | |
will add up, but I&#x27;d gladly wait an extra few seconds or minutes for my tests to | |
finish in exchange for my tests actually giving me confidence that my | |
application will work when I ship it to users.</p><p>In addition to this, you should probably use Jest&#x27;s capabilities for only | |
running tests relevant to your changes while developing your tests so the | |
difference wont be perceivable when running the test suite locally.</p><h3>For actual unit testing</h3><p>This is a very common misconception: &quot;To unit test a react component you must | |
use shallow rendering so other components are not rendered.&quot; It&#x27;s true that | |
shallow rendering doesn&#x27;t render other components (as demonstrated above), | |
what&#x27;s wrong with this is that it&#x27;s way too heavy handed because it doesn&#x27;t | |
render <em>any</em> other components. You don&#x27;t get a choice.</p><p>Not only does shallow rendering not render third party components, it doesn&#x27;t | |
even render in-file components. For example, the <code>&lt;Fade /&gt;</code> component we have | |
above is an implementation detail of the <code>&lt;HiddenMessage /&gt;</code> component, but | |
because we&#x27;re shallow rendering <code>&lt;Fade /&gt;</code> isn&#x27;t rendered so changes to that | |
component could break our application but not our test. That&#x27;s a major issue in | |
my mind and is evidence to me that we&#x27;re testing implementation details.</p><p>In addition, you can <em>definitely</em> unit test react components without shallow | |
rendering. Checkout the section near the end for an example of such a test (uses | |
react-testing-library, but you could do this with enzyme as well) that uses Jest | |
mocking to mock out the <code>&lt;CSSTransition /&gt;</code> component.</p><p>I should add that I generally am against mocking even third party components | |
100% of the time. The argument for mocking third party components I often hear | |
is | |
<a href="https://twitter.com/janhoogeveen/status/1016207627859251200">Testing composed components introduces new dependencies that might trigger an error while the unit itself might still work as intended.</a>. | |
But isn&#x27;t the point of testing to be confident the application works? Who cares | |
if your unit works if the app is broken? I <em>definitely</em> want to know if the | |
third party component I&#x27;m using breaks my use case. I mean, I&#x27;m not going to | |
rewrite their entire test base, but if I can easily test my use case by <em>not</em> | |
mocking out their component then why not do that and get the extra confidence?</p><p>I should also add that | |
<a href="http://kcd.im/write-tests">I&#x27;m in favor of relying more heavily on integration testing</a>. | |
When you do this, you need to unit test fewer of your simple components and wind | |
up only having to unit test edge cases for components (which can mock all they | |
want). But even in these situations, I still think it leads to more confidence | |
and a more maintainable testbase when you&#x27;re explicit about which components are | |
being mocked and which are being rendered by doing full mounting and explicit | |
mocks.</p><h3>Without shallow rendering</h3><p>I&#x27;m a huge believer of the guiding principle of | |
<a href="https://github.com/testing-library/react-testing-library"><code>react-testing-library</code></a>:</p><blockquote><p><a href="https://twitter.com/kentcdodds/status/977018512689455106"><em>The more your tests resemble the way your software is used, the more confidence they can give you.</em></a><em> — Kent | |
C. Dodds 👋</em></p></blockquote><p>That&#x27;s why I wrote the library in the first place. As a side-note to this | |
shallow rendering post, I want to mention there are fewer ways for you to do | |
things that are impossible for the user to do. Here&#x27;s the list of things that | |
react-testing-library cannot do (out of the box):</p><ol><li>shallow rendering</li><li>Static rendering (like enzyme&#x27;s | |
<a href="http://airbnb.io/enzyme/docs/api/render.html"><code>render</code></a> function).</li><li>Pretty much most of enzyme&#x27;s methods to query elements (like | |
<a href="http://airbnb.io/enzyme/docs/api/ReactWrapper/find.html"><code>find</code></a>) which | |
include the ability to find by a component class or even its <code>displayName</code> | |
(again, the user does not care what your component is called and neither | |
should your test). Note: react-testing-library supports querying for | |
elements in ways that encourage accessibility in your components and more | |
maintainable tests.</li><li>Getting a component instance (like enzyme&#x27;s | |
<a href="http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html"><code>instance</code></a>)</li><li>Getting and setting a component&#x27;s props (<code>props()</code>)</li><li>Getting and setting a component&#x27;s state (<code>state()</code>)</li></ol><p>All of these things are things which users of your component cannot do, so your | |
tests shouldn&#x27;t do them either. Below is a test of the <code>&lt;HiddenMessage /&gt;</code> | |
component which resembles the way a user would use your component much more | |
closely. In addition, it can verify that you&#x27;re using <code>&lt;CSSTransition /&gt;</code> | |
properly (something the shallow rendering example was <em>incapable</em> of doing).</p><pre><code class="language-jsx">import &#x27;react-testing-library/cleanup-after-each&#x27; | |
import React from &#x27;react&#x27; | |
import {CSSTransition} from &#x27;react-transition-group&#x27; | |
import {render, fireEvent} from &#x27;react-testing-library&#x27; | |
import {HiddenMessage} from &#x27;../hidden-message&#x27; | |
// NOTE: you do NOT have to do this in every test. | |
// Learn more about Jest&#x27;s __mocks__ directory: | |
// https://jestjs.io/docs/en/manual-mocks | |
jest.mock(&#x27;react-transition-group&#x27;, () =&gt; { | |
return { | |
CSSTransition: jest.fn(({children, in: show}) =&gt; (show ? children : null)), | |
} | |
}) | |
test(&#x27;you can mock things with jest.mock&#x27;, () =&gt; { | |
const {getByText, queryByText} = render(&lt;HiddenMessage initialShow={true} /&gt;) | |
const toggleButton = getByText(/toggle/i) | |
const context = expect.any(Object) | |
const children = expect.any(Object) | |
const defaultProps = {children, timeout: 1000, className: &#x27;fade&#x27;} | |
expect(CSSTransition).toHaveBeenCalledWith( | |
{in: true, ...defaultProps}, | |
context, | |
) | |
expect(getByText(/hello world/i)).not.toBeNull() | |
CSSTransition.mockClear() | |
fireEvent.click(toggleButton) | |
expect(queryByText(/hello world/i)).toBeNull() | |
expect(CSSTransition).toHaveBeenCalledWith( | |
{in: false, ...defaultProps}, | |
context, | |
) | |
}) | |
</code></pre><h3>Conclusion</h3><p>A few weeks ago, my <a href="http://kcd.im/devtips">DevTipsWithKent</a> (my weekdaily | |
livestream on <a href="http://kcd.im/youtube">YouTube</a>) I livestreamed | |
&quot;<a href="https://youtu.be/LHUdxkThTM0?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">Migrating from shallow rendering react components to explicit component mocks</a>&quot;. | |
In that I demonstrate some of the pitfalls of shallow rendering I describe above | |
as well as how to use jest mocking instead.</p><p>I hope this is helpful! We&#x27;re all just trying our best to deliver an awesome | |
experience to users. I wish you luck in that endeavor!</p><h4>P.S.</h4><p>Someone brought this up after I emailed my newsletter out:</p><blockquote><p>Shallow wrapper is good to test small independent components. With proper | |
serializer it allows to have clear and understandable snapshots.</p></blockquote><p>I very rarely use snapshot testing with react and I certainly wouldn&#x27;t use it | |
with shallow. That&#x27;s a recipe for implementation details. The whole snapshot is | |
nothing but implementation details (it&#x27;s full of component and prop names that | |
change all the time on refactors). It&#x27;ll fail any time you touch the component | |
and the git diff for the snapshot will look almost identical to the one for your | |
changes to the component.</p><p>This will make people careless about to the snapshot updates because they change | |
all the time. So it&#x27;s basically worthless (almost worse than no tests because it | |
makes you think you&#x27;re covered when you&#x27;re not and you won&#x27;t write proper tests | |
because they&#x27;re in place).</p><p>I do think that snapshots can be useful though. For more about this from me, | |
checkout another blog post:</p><p><a href="https://kentcdodds.com/blog/effective-snapshot-testing"><strong>Effective Snapshot Testing</strong></a></p><p>I hope that helps!</p><p><strong>Learn more about testing from me</strong>:</p><ul><li><a href="https://frontendmasters.com">Frontend Masters</a>: | |
<a href="https://frontendmasters.com/workshops/testing-practices-principles">Testing Practices and Principles</a> | |
&amp; | |
<a href="https://frontendmasters.com/courses/testing-react">Testing React Applications</a></li><li><a href="https://egghead.io/lessons/react-confidently-ship-production-react-apps">Confidently Ship Production React Apps</a> — Something | |
new on <a href="http://egghead.io">egghead.io</a>. It&#x27;s a recording of one of my talks | |
especially for <a href="http://egghead.io">egghead.io</a>. I think you&#x27;ll really enjoy it | |
(and it&#x27;s 🆓)</li><li><a href="https://youtu.be/Fha2bVoC8SE?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Write tests. Not too many. Mostly integration.</a> — My | |
talk at Assert.js conference | |
(<a href="http://kcd.im/write-tests">and here&#x27;s the blog post</a>)</li><li><a href="https://youtu.be/VQZx1Z3sW0E?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Testing Practices and Principles</a> — A | |
recording of my workshop at Assert.js</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://youtu.be/8BNdxFzMeVg&amp;list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">Avoid setState warnings on unmounted React components</a></li><li><a href="https://youtu.be/3AaghqS3W4Y&amp;list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">Magic Move effect with JavaScript</a></li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/why-i-never-use-shallow-rendering">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[What is JSX?]]></title> | |
<description><![CDATA[I think a critical part of understanding how to use React effectively is | |
understanding JavaScript and JavaScript expressions. So I'm going to show you a | |
few examples of JSX and it's compiled version to help give you an idea of how | |
this all works. As…]]></description> | |
<link>https://kentcdodds.com/blog/what-is-jsx</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/what-is-jsx</guid> | |
<pubDate>Mon, 09 Jul 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>I think a critical part of understanding how to use React effectively is | |
understanding JavaScript and JavaScript expressions. So I&#x27;m going to show you a | |
few examples of JSX and it&#x27;s compiled version to help give you an idea of how | |
this all works. As soon as you can compile JSX in your head, you can use the | |
abstraction more powerfully.</p><p>Here&#x27;s our simplest example:</p><blockquote><p>Note, all examples assign to a variable <code>ui</code> just to illustrate that these are | |
regular JavaScript expressions that you can assign to a variable.</p></blockquote><pre><code class="language-jsx">ui = &lt;div id=&quot;root&quot;&gt;Hello world&lt;/div&gt; | |
ui = React.createElement(&#x27;div&#x27;, {id: &#x27;root&#x27;}, &#x27;Hello world&#x27;) | |
</code></pre><p>As shown above, the JSX is compiled to <code>React.createElement</code>. The API to | |
<code>React.createElement</code> is:</p><pre><code class="language-js">function createElement(elementType, props, ...children) {} | |
</code></pre><ul><li><code>elementType</code> can be a string or a function (class) for the type of element to | |
be created</li><li><code>props</code> is an object for the props we want applied to the element (or <code>null</code> | |
if we specify no props)</li><li><code>...children</code> is all the children we want applied to the element too. This is | |
just a convenience and we could write an equivalent to above with:</li></ul><pre><code class="language-js">ui = React.createElement(&#x27;div&#x27;, {id: &#x27;root&#x27;, children: &#x27;Hello world&#x27;}) | |
</code></pre><p>If you have more than one child then you use an array:</p><pre><code class="language-jsx">ui = ( | |
&lt;div&gt; | |
&lt;span&gt;Hello&lt;/span&gt; &lt;span&gt;World&lt;/span&gt; | |
&lt;/div&gt; | |
) | |
ui = React.createElement(&#x27;div&#x27;, { | |
children: [ | |
React.createElement(&#x27;span&#x27;, null, &#x27;Hello&#x27;), | |
&#x27; &#x27;, | |
React.createElement(&#x27;span&#x27;, null, &#x27;World&#x27;), | |
], | |
}) | |
// Note: babel uses the third argument for children: | |
ui = React.createElement( | |
&#x27;div&#x27;, // type | |
null, // props | |
// children are the rest: | |
React.createElement(&#x27;span&#x27;, null, &#x27;Hello&#x27;), | |
&#x27; &#x27;, | |
React.createElement(&#x27;span&#x27;, null, &#x27;World&#x27;), | |
) | |
</code></pre><p>What you get back from a <code>React.createElement</code> call is actually a simple object:</p><pre><code class="language-js">// &lt;div id=&quot;root&quot;&gt;Hello world&lt;/div&gt; | |
{ | |
type: &quot;div&quot;, | |
key: null, | |
ref: null, | |
props: { id: &quot;root&quot;, children: &quot;Hello world&quot; }, | |
_owner: null, | |
_store: {} | |
}; | |
</code></pre><p>When you pass an object like that to <code>ReactDOM.render</code> or any other renderer, | |
it&#x27;s the renderer&#x27;s job to interpret that element object and create DOM nodes or | |
whatever else out of it. Neat right?!</p><p>Here are a few more examples for you:</p><pre><code class="language-jsx">ui = &lt;div&gt;Hello {subject}&lt;/div&gt; | |
ui = React.createElement(&#x27;div&#x27;, null, &#x27;Hello &#x27;, subject) | |
ui = ( | |
&lt;div&gt; | |
{greeting} {subject} | |
&lt;/div&gt; | |
) | |
ui = React.createElement(&#x27;div&#x27;, null, greeting, &#x27; &#x27;, subject) | |
ui = &lt;button onClick={() =&gt; {}}&gt;click me&lt;/button&gt; | |
ui = React.createElement(&#x27;button&#x27;, {onClick: () =&gt; {}}, &#x27;click me&#x27;) | |
ui = &lt;div&gt;{error ? &lt;span&gt;{error}&lt;/span&gt; : &lt;span&gt;good to go&lt;/span&gt;}&lt;/div&gt; | |
ui = React.createElement( | |
&#x27;div&#x27;, | |
null, | |
error | |
? React.createElement(&#x27;span&#x27;, null, error) | |
: React.createElement(&#x27;span&#x27;, null, &#x27;good to go&#x27;), | |
) | |
ui = ( | |
&lt;div&gt; | |
{items.map(i =&gt; ( | |
&lt;span key={i.id}&gt;{i.content}&lt;/span&gt; | |
))} | |
&lt;/div&gt; | |
) | |
ui = React.createElement( | |
&#x27;div&#x27;, | |
null, | |
items.map(i =&gt; React.createElement(&#x27;span&#x27;, {key: i.id}, i.content)), | |
) | |
</code></pre><p>Notice that whatever you put inside <code>{</code> and <code>}</code> is left alone. This is called an | |
interpolation and allows you to dynamically inject variables into the values of | |
props and children. Because of the way this works, the contents of an | |
interpolation must be JavaScript expressions because they&#x27;re essentially the | |
right hand of an object assignment or used as an argument to a function call.</p><h3>Conclusion</h3><p>If you&#x27;d like to play around with this some more, you can try online with | |
Babel&#x27;s online REPL. | |
<a href="http://babeljs.io/repl/#?babili=false&amp;browsers=&amp;build=&amp;builtIns=false&amp;spec=false&amp;loose=false&amp;code_lz=DwEwlgbgfAEgpgGwQewAQHdkCcEmAenGgG4g&amp;debug=false&amp;forceAllTransforms=false&amp;shippedProposals=false&amp;circleciRepo=&amp;evaluate=true&amp;fileSize=false&amp;sourceType=module&amp;lineWrap=false&amp;presets=react%2Cstage-2&amp;prettier=true&amp;targets=&amp;version=6.26.0&amp;envVersion=1.6.2">Start here</a>. | |
Hopefully this helps you understand a little more about how JSX works and how | |
you can use it more effectively. Good luck!</p><p><strong>Learn more about React from me</strong>:</p><ul><li><a href="http://kcd.im/beginner-react">The Beginner&#x27;s Guide to React</a></li><li><a href="http://kcd.im/advanced-react">Advanced React Component Patterns</a> (also on | |
<a href="https://frontendmasters.com/courses/advanced-react-patterns">Frontend Masters</a>).</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://medium.com/merrickchristensen/headless-user-interface-components-565b0c0f2e18">&quot;Headless User Interface Components</a> — &quot;A | |
headless user interface component is a component that offers maximum visual | |
flexibility by providing no interface. &quot;Wait for a second, are you advocating | |
a user interface pattern that doesn&#x27;t have a user interface?&quot; Yes. That is | |
exactly what I&#x27;m advocating.&quot; Brilliant article by my friend | |
<a href="https://twitter.com/iammerrick">Merrick Christensen</a>.</li><li><a href="https://github.com/jackfranklin/vscode-go-to-file">vscode-go-to-file</a> — A | |
plugin that aims to replicate some of Vim&#x27;s &quot;go to file&quot; (<code>gf</code>) functionality | |
by the great <a href="https://twitter.com/Jack_Franklin">Jack Franklin</a></li><li><a href="http://tabb-extension.com">tabb</a> — A Chrome extension to search, save, and | |
manage your tabs, history, and bookmarks written in | |
<a href="https://reasonml.github.io">Reason</a> by my friend | |
<a href="https://twitter.com/ethangodt">Ethan Godt</a></li><li><a href="https://github.com/pichillilorenzo/deps-report">deps-report</a> — Generate | |
reports about dependencies and dependents of your JavaScript/TypeScript files | |
through an AST. It supports import and require statements. By the insightful | |
<a href="https://twitter.com/LorenzoPichilli">Lorenzo Pichilli</a>.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/what-is-jsx">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Test Isolation with React]]></title> | |
<description><![CDATA[Read to the end, I've got some cool things in the "things not to miss" | |
section The inspiration for this newsletter comes from seeing React tests that look like | |
this: So I want to talk about the importance of test isolation and guide you to a | |
better…]]></description> | |
<link>https://kentcdodds.com/blog/test-isolation-with-react</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/test-isolation-with-react</guid> | |
<pubDate>Mon, 02 Jul 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p><strong>Read to the end, I&#x27;ve got some cool things in the &quot;things not to miss&quot; | |
section</strong></p><p>The inspiration for this newsletter comes from seeing React tests that look like | |
this:</p><pre><code class="language-jsx">const utils = render(&lt;Foo /&gt;) | |
test(&#x27;test 1&#x27;, () =&gt; { | |
// use utils here | |
}) | |
test(&#x27;test 2&#x27;, () =&gt; { | |
// use utils here too | |
}) | |
</code></pre><p>So I want to talk about the importance of test isolation and guide you to a | |
better way to write your tests to improve the reliability of the tests, simplify | |
the code, and increase the confidence your tests and provide as well.</p><p>Let&#x27;s take this simple component as an example:</p><pre><code class="language-jsx">import React from &#x27;react&#x27; | |
class Counter extends React.Component { | |
static defaultProps = { | |
initialCount: 0, | |
maxClicks: 3, | |
} | |
initialState = {count: this.props.initialCount} | |
state = this.initialState | |
handleReset = () =&gt; this.setState(this.initialState) | |
handleClick = () =&gt; | |
this.setState(({count}) =&gt; | |
this.clicksAreTooMany(count) ? null : {count: count + 1}, | |
) | |
clicksAreTooMany(count) { | |
return count &gt;= this.props.maxClicks | |
} | |
render() { | |
const {count} = this.state | |
const tooMany = this.clicksAreTooMany(count) | |
return ( | |
&lt;div&gt; | |
&lt;button onClick={this.handleClick} disabled={tooMany}&gt; | |
Count: {count} | |
&lt;/button&gt; | |
{tooMany ? &lt;button onClick={this.handleReset}&gt;reset&lt;/button&gt; : null} | |
&lt;/div&gt; | |
) | |
} | |
} | |
export {Counter} | |
</code></pre><p>Here&#x27;s a rendered version of the component:</p><p><img src="https://kentcdodds.com/0-b5f55287cae6bb4380b4511fb892e6bb.gif" alt="a rendered version of the component"/></p><h3>Our first test suite</h3><p>Let&#x27;s start with a test suite like the one that inspired this newsletter:</p><pre><code class="language-jsx">import &#x27;jest-dom/extend-expect&#x27; // gives us the toHaveTextContent/toHaveAttribute matchers | |
import React from &#x27;react&#x27; | |
import {renderIntoDocument, cleanup, fireEvent} from &#x27;react-testing-library&#x27; | |
import {Counter} from &#x27;../counter&#x27; | |
const {getByText} = renderIntoDocument( | |
&lt;Counter maxClicks={4} initialCount={3} /&gt;, | |
) | |
const counterButton = getByText(/^count/i) | |
afterAll(cleanup) // when all tests are finished, unmount the component | |
test(&#x27;the counter is initialized to the initialCount&#x27;, () =&gt; { | |
expect(counterButton).toHaveTextContent(/3) | |
}) | |
test(&#x27;when clicked, the counter increments the click&#x27;, () =&gt; { | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
}) | |
test(`the counter button is disabled when it&#x27;s hit the maxClicks`, () =&gt; { | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveAttribute(&#x27;disabled&#x27;) | |
}) | |
test(`the counter button does not increment the count when clicked when it&#x27;s hit the maxClicks`, () =&gt; { | |
expect(counterButton).toHaveTextContent(/4) | |
}) | |
test(`the reset button has been rendered and resets the count when it&#x27;s hit the maxClicks`, () =&gt; { | |
fireEvent.click(getByText(/reset/i)) | |
expect(counterButton).toHaveTextContent(/3) | |
}) | |
</code></pre><p>These tests give us 100% coverage of the component and verify exactly what they | |
say they&#x27;ll verify. The problem is that they share mutable state. What is the | |
mutable state they&#x27;re sharing? The component! One test clicks the counter button | |
and the other tests rely on that fact to pass. If we were to delete (or <code>.skip</code>) | |
the test called &quot;when clicked, the counter increments the click&quot; it would break | |
all the following tests:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/110439ee0087a10f5ff10ccedc2476c4/8ff1e/1.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:141.125%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="broken tests" title="broken tests" src="https://kentcdodds.com/static/110439ee0087a10f5ff10ccedc2476c4/8ff1e/1.png" srcSet="https://kentcdodds.com/static/110439ee0087a10f5ff10ccedc2476c4/f4a45/1.png 259w,https://kentcdodds.com/static/110439ee0087a10f5ff10ccedc2476c4/ef0f6/1.png 518w,https://kentcdodds.com/static/110439ee0087a10f5ff10ccedc2476c4/8ff1e/1.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>This is a problem because it means that we can&#x27;t reliably refactor these tests, | |
or run a single test in isolation of the others for debugging purposes because | |
we don&#x27;t know which tests are impacting the functionality of others. It can be | |
really confusing when someone comes in to make changes to one test and other | |
tests start breaking out of nowhere.</p><h3>Better</h3><p>So let&#x27;s try something else and see how that changes things:</p><pre><code class="language-jsx">import &#x27;jest-dom/extend-expect&#x27; | |
import React from &#x27;react&#x27; | |
import {renderIntoDocument, cleanup, fireEvent} from &#x27;react-testing-library&#x27; | |
import {Counter} from &#x27;../counter&#x27; | |
let getByText, counterButton | |
beforeEach(() =&gt; { | |
const utils = renderIntoDocument(&lt;Counter maxClicks={4} initialCount={3} /&gt;) | |
getByText = utils.getByText | |
counterButton = utils.getByText(/^count/i) | |
}) | |
afterEach(cleanup) | |
test(&#x27;the counter is initialized to the initialCount&#x27;, () =&gt; { | |
expect(counterButton).toHaveTextContent(/3) | |
}) | |
test(&#x27;when clicked, the counter increments the click&#x27;, () =&gt; { | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
}) | |
test(`the counter button is disabled when it&#x27;s hit the maxClicks`, () =&gt; { | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveAttribute(&#x27;disabled&#x27;) | |
}) | |
test(`the counter button does not increment the count when clicked when it&#x27;s hit the maxClicks`, () =&gt; { | |
fireEvent.click(counterButton) | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
}) | |
test(`the reset button has been rendered and resets the count when it&#x27;s hit the maxClicks`, () =&gt; { | |
fireEvent.click(counterButton) | |
fireEvent.click(getByText(/reset/i)) | |
expect(counterButton).toHaveTextContent(/3) | |
}) | |
</code></pre><p>With this, each test is completely isolated from the other. We can delete or | |
skip any test and the rest of the tests continue to pass. The biggest | |
fundamental difference here is that each test has its own count instance to work | |
with and it&#x27;s unmounted after each test (<code>afterEach(cleanup)</code>). This | |
significantly reduces the amount of complexity of our tests with minor changes.</p><p>One thing people often say against this approach is that it&#x27;s slower than the | |
previous approach. I&#x27;m not totally sure how to respond to that... Like, how much | |
slower? Like a few milliseconds? In that case, so what? A few seconds? Then your | |
component should probably be optimized because that&#x27;s just terrible. I know it | |
adds up over time, but with the added confidence and improved maintainability of | |
this approach, I&#x27;d gladly wait an extra few seconds to render things this way. | |
In addition, you shouldn&#x27;t often have to run the entire test base anyway thanks | |
to great watch mode support like we have in Jest.</p><h3>Even better</h3><p>So I&#x27;m actually still not super happy with the tests we have above. I&#x27;m not a | |
huge fan of <code>beforeEach</code> and sharing variables between tests. | |
<a href="https://www.briefs.fm/3-minutes-with-kent/27">I feel like they lead to tests that are harder to understand</a>. | |
Let&#x27;s try again:</p><pre><code class="language-jsx">import &#x27;jest-dom/extend-expect&#x27; | |
import React from &#x27;react&#x27; | |
import {renderIntoDocument, cleanup, fireEvent} from &#x27;react-testing-library&#x27; | |
import {Counter} from &#x27;../counter&#x27; | |
afterEach(cleanup) | |
function renderCounter(props) { | |
const utils = renderIntoDocument( | |
&lt;Counter maxClicks={4} initialCount={3} {...props} /&gt;, | |
) | |
const counterButton = utils.getByText(/^count/i) | |
return {...utils, counterButton} | |
} | |
test(&#x27;the counter is initialized to the initialCount&#x27;, () =&gt; { | |
const {counterButton} = renderCounter() | |
expect(counterButton).toHaveTextContent(/3) | |
}) | |
test(&#x27;when clicked, the counter increments the click&#x27;, () =&gt; { | |
const {counterButton} = renderCounter() | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
}) | |
test(`the counter button is disabled when it&#x27;s hit the maxClicks`, () =&gt; { | |
const {counterButton} = renderCounter({ | |
maxClicks: 4, | |
initialCount: 4, | |
}) | |
expect(counterButton).toHaveAttribute(&#x27;disabled&#x27;) | |
}) | |
test(`the counter button does not increment the count when clicked when it&#x27;s hit the maxClicks`, () =&gt; { | |
const {counterButton} = renderCounter({ | |
maxClicks: 4, | |
initialCount: 4, | |
}) | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
}) | |
test(`the reset button has been rendered and resets the count when it&#x27;s hit the maxClicks`, () =&gt; { | |
const {getByText, counterButton} = renderCounter() | |
fireEvent.click(counterButton) | |
fireEvent.click(getByText(/reset/i)) | |
expect(counterButton).toHaveTextContent(/3) | |
}) | |
</code></pre><p>Here we&#x27;ve increased some boilerplate, but now every test is not only isolated | |
technically, but also visually. You can look at a test and see exactly what it | |
does without having to worry about what hooks are happening within the test. | |
This is a big win in the ability for you to be able to refactor, remove, or add | |
to the tests.</p><h3>Even better better</h3><p>I like what we have now, but I think we need to take things one step further | |
before I feel really happy about things. We&#x27;ve split our tests up by | |
functionality, but what we really want to have confidence in is the use case | |
that our component satisfies. It allows clicks until the maxClicks is reached, | |
then requires a reset. That&#x27;s what we&#x27;re trying to verify and gain confidence | |
in. I&#x27;m much more interested in use cases when I&#x27;m testing than specific | |
functionality. So what would these tests look like if we concerned ourselves | |
more with the use case than the individual functionality?</p><pre><code class="language-jsx">import &#x27;jest-dom/extend-expect&#x27; | |
import React from &#x27;react&#x27; | |
import {renderIntoDocument, cleanup, fireEvent} from &#x27;react-testing-library&#x27; | |
import {Counter} from &#x27;../counter&#x27; | |
afterEach(cleanup) | |
test(&#x27;allows clicks until the maxClicks is reached, then requires a reset&#x27;, () =&gt; { | |
const {getByText} = renderIntoDocument( | |
&lt;Counter maxClicks={4} initialCount={3} /&gt;, | |
) | |
const counterButton = getByText(/^count/i) | |
// the counter is initialized to the initialCount | |
expect(counterButton).toHaveTextContent(/3) | |
// when clicked, the counter increments the click | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
// the counter button is disabled when it&#x27;s hit the maxClicks | |
expect(counterButton).toHaveAttribute(&#x27;disabled&#x27;) | |
// the counter button no longer increments the count when clicked. | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
// the reset button has been rendered and is clickable | |
fireEvent.click(getByText(/reset/i)) | |
// the counter is reset to the initialCount | |
expect(counterButton).toHaveTextContent(/3) | |
// the counter can be clicked and increment the count again | |
fireEvent.click(counterButton) | |
expect(counterButton).toHaveTextContent(/4) | |
}) | |
</code></pre><p>I really love this kind of test. It helps me avoid thinking about functionality | |
and focus more on what I&#x27;m trying to accomplish with the component. It serves as | |
much better documentation of the component than the other tests as well.</p><p>In the past, the reason we wouldn&#x27;t do this (have multiple assertions in a | |
single test) is because it was hard to tell which part of the test broke. But | |
now we have much better error output and it&#x27;s really easy to identify what part | |
of the test broke. For example:</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<a class="gatsby-resp-image-link" href="https://kentcdodds.com/static/8ad6d8b27848cd42899f8ac068f9f195/8ff1e/2.png" style="display:block" target="_blank" rel="noopener"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:57.50000000000001%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="broken tests" title="broken tests" src="https://kentcdodds.com/static/8ad6d8b27848cd42899f8ac068f9f195/8ff1e/2.png" srcSet="https://kentcdodds.com/static/8ad6d8b27848cd42899f8ac068f9f195/f4a45/2.png 259w,https://kentcdodds.com/static/8ad6d8b27848cd42899f8ac068f9f195/ef0f6/2.png 518w,https://kentcdodds.com/static/8ad6d8b27848cd42899f8ac068f9f195/8ff1e/2.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</a> | |
</span></p><p>The code frame is especially helpful. It shows not only the line number, but the | |
code around the failed assertion which shows our comments and other code to | |
really help give us context around the error message that not even our previous | |
tests gave us.</p><p>I should mention, this isn&#x27;t to say that you shouldn&#x27;t separate test cases for a | |
component! There are many reasons you&#x27;d want to do that and most of the time you | |
will. Just focus more on use cases than functionality and you&#x27;ll generally cover | |
most of the code you care about with that. Then you can have a few extra tests | |
to handle edge cases.</p><h3>Conclusion</h3><p>I hope this is helpful to you! You can find the code for this example | |
<a href="https://github.com/kentcdodds/react-test-isolation">here</a>. Try to keep your | |
tests isolated from one another and focus on use cases rather than functionality | |
and you&#x27;ll have a much better time testing! Good luck!</p><p><strong>Learn more about testing from me</strong>:</p><ul><li><a href="https://frontendmasters.com">Frontend Masters</a>: | |
<a href="https://frontendmasters.com/workshops/testing-practices-principles">Testing Practices and Principles</a>, | |
<a href="https://frontendmasters.com/courses/testing-react">Testing React Applications</a></li><li><a href="https://egghead.io/lessons/react-confidently-ship-production-react-apps">Confidently Ship Production React Apps</a> — Something | |
new on <a href="http://egghead.io">egghead.io</a>. It&#x27;s a recording of one of my talks | |
especially for <a href="http://egghead.io">egghead.io</a>. I think you&#x27;ll really enjoy it | |
(and it&#x27;s 🆓)</li><li><a href="https://youtu.be/Fha2bVoC8SE?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Write tests. Not too many. Mostly integration.</a> — My | |
talk at Assert.js conference | |
(<a href="http://kcd.im/write-tests">and here&#x27;s the blog post</a>)</li><li><a href="https://youtu.be/VQZx1Z3sW0E?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Testing Practices and Principles</a> — A | |
recording of my workshop at Assert.js</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://medium.com/byteconf/byteconf-react-speakers-round-one-ab25af8edf23">Byteconf React Speakers: Round One</a> — Look! | |
It&#x27;s a free online conference and I&#x27;m speaking at it!</li><li><a href="https://kentcdodds.com/blog/downshift-2-0-0-released">🏎 downshift 2.0.0 released 🎉</a> — Even better | |
accessibility, React Native and ReasonReact support, even simpler API, | |
improved docs, new examples site, Flow and TypeScript support, and a new | |
online community ⚛️</li><li><a href="http://keycode.info">keycode.info</a> by | |
<a href="https://twitter.com/wesbos">Wes Bos</a> — shows you the javascript character | |
code for the key you type. Handy!</li><li><a href="https://store.google.com/us/product/google_pixelbook">Chrome Pixelbook</a> — It&#x27;s | |
what I&#x27;m using to write this right now and it&#x27;s pretty slick!</li><li><a href="https://medium.com/@Daajust/testing-socket-io-client-app-using-jest-and-react-testing-library-9cae93c070a3">Testing Socket.io-client app using Jest and react-testing-library</a> | |
by my friend <a href="https://twitter.com/Daajust">Justice Mba</a>.</li><li><a href="https://medium.com/@hemal7735/webpack-4-splitchunks-plugin-d9fbbe091fd0">Webpack 4 — Mysterious SplitChunks Plugin</a> — My | |
fellow PayPal engineer <a href="https://twitter.com/TheHemalPatel">Hemal Patel</a> wrote | |
about how the <code>splitChunks.chunks</code> feature works. Pretty interesting!</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/test-isolation-with-react">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[JavaScript default parameters]]></title> | |
<description><![CDATA[Today I thought I'd take you through one of the examples from | |
my es6 workshop . Consider the following code: It's fairly simple, but there are potential bugs (read about | |
Falsy | |
on MDN ) and some | |
annoying boilerplate. Luckily for us, ES6 introduced…]]></description> | |
<link>https://kentcdodds.com/blog/javascript-default-parameters</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/javascript-default-parameters</guid> | |
<pubDate>Mon, 25 Jun 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>Today I thought I&#x27;d take you through one of the examples from | |
<a href="https://github.com/kentcdodds/es6-workshop">my es6 workshop</a>.</p><p>Consider the following code:</p><pre><code class="language-js">function getCandy(kind, size, upperKind, callback) { | |
if (!kind) { | |
requiredParam(&#x27;kind&#x27;) | |
} | |
if (!size) { | |
requiredParam(&#x27;size&#x27;) | |
} | |
upperKind = upperKind || kind.toUpperCase() | |
callback = callback || function noop() {} | |
const result = {kind, size, upperKind} | |
callback(result) | |
return result | |
} | |
function requiredParam(argName) { | |
throw new Error(`${argName} is required`) | |
} | |
</code></pre><p>It&#x27;s fairly simple, but there are potential bugs (read about | |
<a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy"><code>Falsy</code></a> | |
<a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">on MDN</a>) and some | |
annoying boilerplate. Luckily for us, ES6 introduced new syntax into JavaScript | |
that we can use to simplify things a bit. In particular: | |
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">default parameters</a>. | |
Let&#x27;s checkout what the above would be like when using this feature.</p><pre><code class="language-js">function getCandy( | |
kind = requiredParam(&#x27;kind&#x27;), | |
size = requiredParam(&#x27;size&#x27;), | |
upperKind = kind.toUpperCase(), | |
callback = function noop() {}, | |
) { | |
const result = {kind, size, upperKind} | |
callback(result) | |
return result | |
} | |
function requiredParam(argName) { | |
throw new Error(`${argName} is required`) | |
} | |
</code></pre><p>Notice that we&#x27;re able to take each expression and put it on the right side of | |
the equals sign. If the parameter is <code>undefined</code>then the expression on the right | |
side will be evaluated. This allows us to only call the <code>requiredParam</code> function | |
if <code>kind</code> or <code>size</code> is <code>undefined</code>. It also is possible to use the value of | |
other parameters in our expression like we do in the default param for | |
<code>upperKind</code> which I find to be a ridiculously cool feature and I use this all | |
the time in options configuration for some of my tools | |
(<a href="https://github.com/babel-utils/babel-plugin-tester/blob/4b512e895a8934cdc6bb54be3be3241d56cfb9dc/src/index.js#L25-L28">for example</a>).</p><p>I&#x27;ll add that the same kinds of semantics would apply for object destructuring | |
(whether as a parameter or not) as well. For example, if we change the arguments | |
to be an options object:</p><pre><code class="language-js">function getCandy(options = {}) { | |
const { | |
kind = requiredParam(&#x27;kind&#x27;), | |
size = requiredParam(&#x27;size&#x27;), | |
upperKind = kind.toUpperCase(), | |
callback = function noop() {}, | |
} = options | |
// etc... | |
} | |
</code></pre><p>Or, if we want to destructure the options object directly in the parameter list:</p><pre><code class="language-js">function getCandy({ | |
kind = requiredParam(&#x27;kind&#x27;), | |
size = requiredParam(&#x27;size&#x27;), | |
upperKind = kind.toUpperCase(), | |
callback = function noop() {}, | |
} = {}) { | |
// etc... | |
} | |
</code></pre><p>Fun stuff!</p><h3>Conclusion</h3><p>I hope you find this helpful! If you&#x27;d like to watch me talk about this a bit, | |
you can check out this section of my ES6 workshop I gave and recorded at PayPal | |
a while back: | |
<a href="https://youtu.be/t3R3R7UyN2Y">ES6 and Beyond Workshop Part 1 at PayPal (Jan 2017)</a>. | |
Good luck!</p><p><strong>Learn more about JavaScript from me</strong>:</p><ul><li><a href="https://youtu.be/kTlcu16rSLc?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">More than you want to know about ES6 Modules @ Learn to Code Websites and Apps Meetup (remote)</a></li><li><a href="https://youtu.be/t3R3R7UyN2Y?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">ES6 and Beyond Workshop Part 1 at PayPal (Jan 2017)</a></li><li><a href="https://youtu.be/eOKQDh50ECU?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">ES6 and Beyond Workshop Part 2 at PayPal (March 2017)</a></li><li><a href="https://kentcdodds.com/workshops/#code-transformation-and-linting">Code Transformation and Linting</a></li><li><a href="https://kentcdodds.com/talks/#writing-custom-babel-and-eslint-plugins-with-asts">Writing custom Babel and ESLint plugins with ASTs</a></li></ul><p>Also, don&#x27;t forget to subscribe to <a href="http://kcd.im/youtube">my youtube channel</a> | |
for my daily devtips, like | |
<a href="https://youtu.be/FsgGx1SMXn0?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">the one today where</a> | |
I demo some advanced features of destructuring!</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://semver-calc.now.sh">Semver Calc</a> — A sweet new app by | |
<a href="https://twitter.com/tarang9211">Tarang Hirani</a> and | |
<a href="https://twitter.com/__shriram">Shriram Balaji</a>. It allows you to try out | |
semver ranges on packages versions to fine-tune the version range. Especially | |
useful for <code>peerDependencies</code>.</li><li><a href="https://www.anxietytech.com">AnxietyTech</a> — A conference on July 18th, 2018 | |
to bring awareness of mental health issues in tech. Use code <code>TECH</code> for \$25 | |
off.</li><li><a href="https://github.com/sindresorhus/refined-twitter">Refined Twitter</a> — Browser | |
extension that simplifies the Twitter interface and adds useful features. I | |
love it.</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/javascript-default-parameters">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Dealing with FOMO]]></title> | |
<description><![CDATA[🚨 Announcements 🚨 Advanced React Online and | |
Live! Join me in my online workshop this week! Get your questions answered in | |
real-time and have time to work through exercises to up your React game. DevTips with Kent — I'm taking a break this week…]]></description> | |
<link>https://kentcdodds.com/blog/dealing-with-fomo</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/dealing-with-fomo</guid> | |
<pubDate>Mon, 18 Jun 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>🚨 <strong>Announcements</strong> 🚨</p><ul><li><a href="https://workshop.me/2018-06-advanced-react?a=kent">Advanced React Online</a> and | |
Live! Join me in my online workshop this week! Get your questions answered in | |
real-time and have time to work through exercises to up your React game.</li><li><a href="http://kcd.im/devtips">DevTips with Kent</a> — I&#x27;m taking a break this week | |
because I&#x27;m on vacation with my family. Feel free to enjoy the recordings | |
until I get back next week!</li></ul><p>If you&#x27;re not familiar with the acronym FOMO that&#x27;s totally fine (and also a | |
tiny bit ironic). FOMO stands for &quot;Fear Of Missing Out.&quot; You might feel this for | |
example if you&#x27;re deciding whether to learn | |
<a href="https://reasonml.github.io">ReasonML</a> or keep going deep into JavaScript (this | |
happens to be something that I&#x27;m currently struggling with).</p><h3>What&#x27;s wrong with FOMO</h3><p>FOMO is paralyzing. I once entered a conversation with a man at a conference who | |
was very concerned about which JavaScript framework to use for building | |
applications. He asked me how I could possibly decide. I just said that I tried | |
a few and chose one that I thought was pretty good and went with it.</p><p>He couldn&#x27;t grasp it at all and showed me a Google Spreadsheet he&#x27;d created that | |
would probably take half a tree to print (a small tree probably). It had columns | |
for every framework imaginable and rows for every conceivable feature. I&#x27;m | |
telling you, this thing was HUGE.</p><p>Even with this exhaustive comparison, he still couldn&#x27;t decide. He was worried | |
about making the wrong decision. We call this analysis paralysis and I actually | |
mentioned to him that I thought he&#x27;d hit that point which he denied. Whether or | |
not that really was the case, he was emotionally consumed by this decision.</p><h3>Where does FOMO come from?</h3><p>FOMO is entirely based on comparing one&#x27;s self with others. These kinds of | |
comparisons are entirely unhealthy and insatiable. No matter how much knowledge | |
or experience you gain, there will always be someone who has more and you will | |
be left feeling inadequate.</p><p>The reason for this is we often make the mistake of thinking there are only two | |
people in the world: You, and everyone else. While this entirely false, it&#x27;s an | |
easy pit to fall into and leads to FOMO and feelings that we can never measure | |
up to where everyone who is not us is at.</p><h3>How to manage</h3><p>Knowing that FOMO comes from the unhealthy tendency to compare ourselves to | |
others, managing and gaining control of FOMO becomes a task of controlling that | |
tendency. I&#x27;m still working on this myself, but I think a great first step is to | |
be mindful of yourself and the thoughts you&#x27;re having about others and yourself. | |
Try to catch yourself thinking thoughts like: &quot;I&#x27;m better than them at x&quot; or | |
&quot;Wow, I&#x27;ll never be that good at x as they are.&quot; However true those thoughts | |
are, they are not helpful or compassionate to yourself or others.</p><p>Instead, consciously train your brain to think things like: &quot;How can I help | |
others learn what I know?&quot; or &quot;Wow! That&#x27;s really cool that they&#x27;re so talented | |
at that!&quot; There&#x27;s nothing wrong with a healthy desire to learn more, and you can | |
train your brain to think positively about your ability to improve. Changing the | |
tone and attitude of your own self-talk can really make a positive impact of how | |
you feel about yourself and others. You&#x27;ll feel more motivated to improve and | |
feel empowered to do so.</p><h3>Conclusion</h3><p>I think it&#x27;s really important for you to know that your brain is a muscle and | |
you can exercise different parts of it. Try to exercise the parts of your brain | |
that help you be more compassionate to yourself and others and you&#x27;ll reduce | |
your unhealthy FOMO and develop a healthy amount of MTBB (Motivation To Become | |
Better... I just made that up).</p><p>I hope that&#x27;s helpful to you. Like I said, this is something I&#x27;m actively | |
working on in myself, I hope that together we can work on improving this aspect | |
of ourselves and find more happiness in life :) Good luck!</p><p><strong>Things to not miss</strong>:</p><ul><li><a href="http://kcd.im/advanced-react"><strong>UPDATED</strong>: Advanced React Component Patterns</a> — This | |
is the <a href="http://egghead.io">egghead.io</a> version of my course material | |
completely updated for the latest in React with <strong>NEW PATTERNS</strong> (like | |
<a href="https://kentcdodds.com/blog/the-state-reducer-pattern">the state reducer prop pattern</a>). Enjoy!</li><li><a href="https://youtu.be/vVlcq3e1ooI">Drive with Kent — Tech phone call recording</a> — In | |
case you missed it, | |
<a href="https://github.com/kentcdodds/ama/issues/405">I took a 6 hour drive</a> and had | |
a phone conversation with people about React, Testing, CSS-in-JS, and more. | |
Check it out!</li><li><a href="https://reach.tech/router">reach-router</a> — Not a typo, this is a new router | |
from <a href="https://twitter.com/ryanflorence">Ryan Florence</a> (creator of | |
react-router and many others). It&#x27;s pretty neat and has great docs. I did | |
<a href="https://youtu.be/J1vsBrSUptA?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">a devtip</a> | |
with it the other day.</li><li><a href="http://www.richardkotze.com/coding/react-testing-library-jest">react-testing-library &amp; Jest</a></li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/dealing-with-fomo">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[🏎 downshift 2.0.0 released 🎉]]></title> | |
<description><![CDATA[Even better accessibility, React Native and ReasonReact support, even simpler | |
API, improved docs, new examples site, Flow and TypeScript support, and a new | |
online community ⚛️ I'm excited to let the world know that downshift 2.0.0 has been released…]]></description> | |
<link>https://kentcdodds.com/blog/downshift-2-0-0-released</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/downshift-2-0-0-released</guid> | |
<pubDate>Fri, 15 Jun 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p><em>Even better accessibility, React Native and ReasonReact support, even simpler | |
API, improved docs, new examples site, Flow and TypeScript support, and a new | |
online community ⚛️</em></p><p>I&#x27;m excited to let the world know that downshift 2.0.0 has been released! So I&#x27;m | |
going to do it now:</p><blockquote><p>Hey world! downshift 2.0.0 has been released!</p></blockquote><p>Woo! So what do you have to look forward to? Let&#x27;s dive in! (If you haven&#x27;t | |
heard of downshift, consider reading | |
<a href="https://kentcdodds.com/blog/introducing-downshift-for-react">the original release post</a>).</p><h3>Improved Accessibility (<a href="https://twitter.com/hashtag/a11y"><strong>#a11y</strong></a>)</h3><p>Lead primarily by <a href="https://github.com/cycomachead">Michael Ball</a> (with helpful | |
reviews from several others), we | |
<a href="https://github.com/downshift-js/downshift/pull/285">received</a> a number of | |
improvements to the accessibility features baked-into downshift. He also added a | |
new | |
<a href="https://github.com/downshift-js/downshift/blob/master/README.md#getmenuprops"><code>getMenuProps</code></a> | |
<a href="https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters">prop getter</a> | |
(which was also instrumental in fixing | |
<a href="https://github.com/downshift-js/downshift/issues/287">a bug with React Portals</a>). | |
This allows us to add some <code>aria-</code> attributes to the menu you render that will | |
help assistive technologies use your enhanced input components! Woo! I&#x27;ve also | |
updated many of the examples to use more semantically correct elements.</p><h3>React Native Support</h3><p>We&#x27;ve actually had this for a while in 1.x, but I wanted to include this in the | |
blog post because there was never really an official announcement and I think | |
that this is great! This is largely thanks to work by | |
<a href="https://github.com/eliperkins">Eli Perkins</a>! They&#x27;ve already shipped downshift | |
to production in their iOS app:</p><p><a href="https://github.com/downshift-js/downshift/issues/185#issuecomment-365965566"><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:800px"> | |
<span class="gatsby-resp-image-background-image" style="padding-bottom:93.875%;position:relative;bottom:0px;left:0px;background-size:cover;display:block"></span> | |
<img class="gatsby-resp-image-image" alt="Screenshot of downshift code in React Native from Eli" title="Screenshot of downshift code in React Native from Eli" src="https://kentcdodds.com/static/4276bfc11a9fe5811860d718d994a418/8ff1e/0.png" srcSet="https://kentcdodds.com/static/4276bfc11a9fe5811860d718d994a418/f4a45/0.png 259w,https://kentcdodds.com/static/4276bfc11a9fe5811860d718d994a418/ef0f6/0.png 518w,https://kentcdodds.com/static/4276bfc11a9fe5811860d718d994a418/8ff1e/0.png 800w" sizes="(max-width: 800px) 100vw, 800px"/> | |
</span></a></p><h3>ReasonReact Support</h3><p>We&#x27;ve actually had this for a while too, but I want to call it out especially. | |
Thanks to <a href="https://github.com/emmenko">Nicola Molinari</a> there are | |
<a href="https://github.com/downshift-js/downshift/blob/master/README.md#bindings-for-reasonml">official Reason bindings for downshift</a>. | |
So you can build UIs for any platform you can imagine with downshift and | |
<a href="https://reasonml.github.io">ReasonML</a>. Soooo cool!</p><h3>Simpler API</h3><p>It&#x27;s generally a good idea to avoid APIs which allow two ways to do the same | |
thing. It&#x27;s just another thing to list in docs, and you have to help people | |
understand what the differences are (if there are any).</p><p>Up until downshift 2.0.0, we had two props that could be used for your | |
<a href="https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce">render prop</a>: | |
<code>render</code> and <code>children</code>. Then React released the official context API which uses | |
render props and they called it <code>children</code>. In an effort to develop consistency | |
throughout the ecosystem, we&#x27;ve dropped the prop called <code>render</code> and now only | |
support <code>children</code>.</p><h3>Improved Docs</h3><p>After downshift was out for a while, I started to realize that folks missed out | |
on some of the more useful and necessary props (like | |
<a href="https://github.com/downshift-js/downshift/blob/master/README.md#itemtostring"><code>itemToString</code></a>). | |
I blame myself for this and recently reorganized the docs to make more relevant | |
information more apparent.</p><h3>New Examples Site</h3><p>It&#x27;s just getting started (so there are only a few examples), but it&#x27;s here and | |
ready for contributions!</p><p>Find it and contribute | |
(<a href="https://hackernoon.com/announcing-codesandbox-2-0-938cff3a0fcb">right in the browser!</a>) | |
on codesandbox!</p><ul><li>Codesandbox: | |
<a href="https://codesandbox.io/s/github/kentcdodds/downshift-examples">codesandbox.io/s/github/kentcdodds/downshift-examples</a></li><li>GitHub: | |
<a href="https://github.com/kentcdodds/downshift-examples">https://github.com/kentcdodds/downshift-examples</a></li></ul><figcaption>The codesandbox for the examples site</figcaption><h3>Improved TypeScript Support</h3><p>A great effort lead primarily by | |
<a href="https://github.com/stereobooster">@stereobooster</a> has lead to improved | |
TypeScript definitions for downshift. Even if you don&#x27;t use TypeScript, you&#x27;ll | |
benefit from these typings as they will help ensure we don&#x27;t publish breaking | |
changes unknowingly and if you use VSCode you&#x27;ll benefit from these typings with | |
the built-in Intellisense!</p><h3>Support for Flow!</h3><p>Another awesome contribution by | |
<a href="https://github.com/stereobooster">@stereobooster</a>. We&#x27;re now generating flow | |
type definitions from the TypeScript definitions, so if you&#x27;re on the Flow | |
train, you&#x27;ll get more type-safety when working with downshift!</p><h3>We&#x27;re on spectrum!</h3><p><a href="https://spectrum.chat/downshift"><strong>downshift</strong></a></p><p>Yeehaw! Join us there to talk about the present and future of downshift and get | |
help/help others.</p><h3>Thanks</h3><p>This release and these amazing features would not be possible without help from | |
all our open source contributors. I&#x27;d like to especially thank these awesome | |
people:</p><p><a href="https://github.com/stereobooster">@stereobooster</a>, | |
<a href="https://github.com/franklixuefei">@franklixuefei</a>, | |
<a href="https://github.com/dovidweisz">@dovidweisz</a>, | |
<a href="https://github.com/Antontelesh">@Antontelesh</a>, | |
<a href="https://github.com/tansongyang">@tansongyang</a>, | |
<a href="https://github.com/Andarist">@Andarist</a>, | |
<a href="https://github.com/cycomachead">@cycomachead</a>, | |
<a href="https://github.com/mperrotti">@mperrotti</a>, | |
<a href="https://github.com/SiTaggart">@SiTaggart</a>, and | |
<a href="https://github.com/1Copenut">@1Copenut</a></p><p>Thank you!</p><p><a href="https://github.com/downshift-js/downshift/releases/tag/v2.0.0"><em>See the release notes for more info on this release</em></a></p> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/downshift-2-0-0-released">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[When to use Control Props or State Reducers]]></title> | |
<description><![CDATA[You’ve probably used components or elements that implement the control props | |
pattern. For example: Read more about the concept of control props in | |
the react docs . You may not have had much experience with the idea of a | |
state reducer . In contrast…]]></description> | |
<link>https://kentcdodds.com/blog/control-props-vs-state-reducers</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/control-props-vs-state-reducers</guid> | |
<pubDate>Mon, 11 Jun 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>You’ve probably used components or elements that implement the control props | |
pattern. For example:</p><pre><code class="language-jsx">&lt;input value={this.state.inputValue} onChange={this.handleInputChange} /&gt; | |
</code></pre><p>Read more about the concept of control props in | |
<a href="https://reactjs.org/docs/forms.html">the react docs</a>.</p><p>You may not have had much experience with the idea of a | |
<a href="https://kentcdodds.com/blog/the-state-reducer-pattern">state reducer</a>. In contrast to control props, | |
built-in react elements don’t support state reducers (though I hear that | |
reason-react does). My library | |
<a href="https://github.com/downshift-js/downshift">downshift</a> supports a state reducer. | |
Here’s an example of using it to prevent the menu from closing after an item is | |
selected:</p><pre><code class="language-jsx">function stateReducer(state, changes) { | |
if (changes.type === Downshift.stateChangeTypes.clickItem) { | |
// when the user clicks an item, prevent | |
// keep the isOpen to true | |
return {...changes, isOpen: true} | |
} | |
return changes | |
} | |
const ui = ( | |
&lt;Downshift stateReducer={stateReducer}&gt; | |
{() =&gt; &lt;div&gt;{/* some ui stuff */}&lt;/div&gt;} | |
&lt;/Downshift&gt; | |
) | |
</code></pre><p>You can learn how to implement these patterns from | |
<a href="https://kentcdodds.com/workshops/#advanced-react-component-patterns">my Advanced React Component Patterns material</a>.</p><p>Both of these patterns help you expose state management to component consumers | |
and while they have significantly different APIs, they allow much of the same | |
capabilities. So today I’d like to answer the question I’ve gotten many times | |
which is: “When should I expose a state reducer or a control prop?”</p><p>Control Props are objectively more powerful because they allow complete control | |
over state from outside the component. Let’s take my favorite Toggle component | |
as an example:</p><pre><code class="language-jsx">class Example extends React.Component { | |
state = {on: false, inputValue: &#x27;off&#x27;} | |
handleToggle = on =&gt; { | |
this.setState({on, inputValue: on ? &#x27;on&#x27; : &#x27;off&#x27;}) | |
} | |
handleChange = ({target: {value}}) =&gt; { | |
if (value === &#x27;on&#x27;) { | |
this.setState({on: true}) | |
} else if (value === &#x27;off&#x27;) { | |
this.setState({on: false}) | |
} | |
this.setState({inputValue: value}) | |
} | |
render() { | |
const {on} = this.state | |
return ( | |
&lt;div&gt; | |
{/* | |
here we&#x27;re using the `value` control prop | |
exposed by the &lt;input /&gt; component | |
*/} | |
&lt;input value={this.state.inputValue} onChange={this.handleChange} /&gt; | |
{/* | |
here we&#x27;re using the `on` control prop | |
exposed by the &lt;Toggle /&gt; component. | |
*/} | |
&lt;Toggle on={on} onToggle={this.handleToggle} /&gt; | |
&lt;/div&gt; | |
) | |
} | |
} | |
</code></pre><p>Here’s a rendered version of this component:</p><p><img src="https://kentcdodds.com/toggle1-ae163064b48faf762855da446b06f9b2.gif" alt="gif of the rendered component showing an input and toggle that sync their state"/></p><p>As you can see, I can control the state of the toggle button by changing the | |
text of the input component, and control the state of the input by clicking on | |
the toggle. This is powerful because it allows me to have complete control over | |
the state of these components.</p><p>Control props do come with a cost however. They require that the consumer | |
completely manage state themselves which means the consumer must have a class | |
component with state and change handlers to update that state.</p><p>State reducers do not have to manage the component’s state themselves (though | |
they can manage some of their own state as needed). Here’s an example of using a | |
state reducer:</p><pre><code class="language-jsx">class Example extends React.Component { | |
initialState = {timesClicked: 0} | |
state = this.initialState | |
handleToggle = (...args) =&gt; { | |
this.setState(({timesClicked}) =&gt; ({ | |
timesClicked: timesClicked + 1, | |
})) | |
} | |
handleReset = (...args) =&gt; { | |
this.setState(this.initialState) | |
} | |
toggleStateReducer = (state, changes) =&gt; { | |
if (this.state.timesClicked &gt;= 4) { | |
return {...changes, on: false} | |
} | |
return changes | |
} | |
render() { | |
const {timesClicked} = this.state | |
return ( | |
&lt;div&gt; | |
&lt;Toggle | |
stateReducer={this.toggleStateReducer} | |
onToggle={this.handleToggle} | |
onReset={this.handleReset} | |
/&gt; | |
{timesClicked &gt; 4 ? ( | |
&lt;div&gt; | |
Whoa, you clicked too much! | |
&lt;br /&gt; | |
&lt;/div&gt; | |
) : ( | |
&lt;div&gt;Click count: {timesClicked}&lt;/div&gt; | |
)} | |
&lt;/div&gt; | |
) | |
} | |
} | |
</code></pre><p>And here’s a gif of the rendered interaction.</p><p><img src="https://kentcdodds.com/toggle2-92c108bac456b9786c0be135dd7b811e.gif" alt="gif of the rendered component showing a toggle, reset button, and counter that’s limited to 4 toggles."/></p><p>Now, you could definitely implement this experience using a control prop, but I | |
would argue that it’s a fair bit simpler if you can use the state reducer. The | |
biggest limitation of a state reducer is that it’s impossible to set state of | |
the component from outside it’s normal <code>setState</code> calls (I couldn&#x27;t implement | |
the first example using a state reducer).</p><p>I hope this is helpful! Feel free to see the implementation and play around with | |
things in <a href="https://codesandbox.io/s/n09418kvr0">this codesandbox</a>.</p><p>Good luck!</p><p><strong>Learn more about React from me</strong>:</p><ul><li><a href="http://kcd.im/beginner-react">egghead.io (beginners)</a> — My Beginner’s Guide | |
to React absolutely <em>free</em> on <a href="http://egghead.io">egghead.io</a>.</li><li><a href="http://kcd.im/advanced-react">egghead.io (advanced)</a> — My Advanced React | |
Component Patterns course available on <a href="http://egghead.io">egghead.io</a> today!</li><li><a href="https://frontendmasters.com/workshops/advanced-react-patterns">Frontend Masters</a> — My | |
Advanced React Patterns workshop</li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://youtube.com/playlist?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u"><strong>DevTips with Kent</strong></a> | |
❗️❗️❗️ I’ve started a new series of daily short videos about software | |
development. I livestream them every weekday. Check out the playlist of videos | |
I have up there already including | |
<a href="https://youtu.be/Dli_FisDdVU?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">npm tips</a>, | |
<a href="https://youtu.be/kCR3JAR7CHE?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">TDD with react-testing-library</a>, | |
<a href="https://youtu.be/JGXAvgVHC5A?list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u">webpack HMR</a>, | |
and more!</li><li><a href="https://medium.com/@ken_wheeler/a-bitter-guide-to-open-source-a8e3b6a3c1c4">A bitter guide to open source</a> | |
by <a href="https://twitter.com/ken_wheeler">Ken Wheeler</a>. It’s <strong>incredibly</strong> | |
insightful (and full of cursing, you’ve been warned).</li></ul> | |
<div> | |
<div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<div style="display: flex;"> | |
<div style="padding-right: 20px;"> | |
<img | |
src="https://kentcdodds.com/images/small-circular-kent.png" | |
alt="Kent C. Dodds" | |
style="max-width: 80px; border-radius: 50%;" | |
/> | |
</div> | |
<p> | |
<strong>Kent C. Dodds</strong> 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. | |
</p> | |
</div> | |
<div> | |
<p>Learn more with Kent C. Dodds:</p> | |
<ul> | |
<li> | |
<a href="https://kentcdodds.com/workshops">Live, professional workshops</a>: | |
Join Kent C. Dodds from the comfort of your home for live remote workshops. | |
Tickets are limited! 🎟 | |
</li> | |
<li> | |
<a href="https://testingjavascript.com">TestingJavaScript.com</a>: Jump on | |
this self-paced workshop and learn the smart, efficient way to test any | |
JavaScript application. 🏆 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div><div style="margin-top=55px; font-style: italic;">(This article was posted to my blog at <a href="https://kentcdodds.com/blog">https://kentcdodds.com/blog</a>. You can <a href="https://kentcdodds.com/blog/control-props-vs-state-reducers">read it online by clicking here</a>.)</div> | |
</div></content:encoded> | |
</item> | |
<item> | |
<title><![CDATA[Write your own code transform for fun and profit]]></title> | |
<description><![CDATA[If you haven't heard, | |
babel-plugin-macros | |
"enables zero-config, importable babel plugins." A few months ago, I published a | |
blog post about it on the official babel blog: | |
"Zero-config code transformation with babel-plugin-macros" . Since then…]]></description> | |
<link>https://kentcdodds.com/blog/write-your-own-code-transform</link> | |
<guid isPermaLink="false">https://kentcdodds.com/blog/write-your-own-code-transform</guid> | |
<pubDate>Mon, 04 Jun 2018 00:00:00 GMT</pubDate> | |
<content:encoded><div style="width: 100%; margin: 0 auto; max-width: 800px; padding: 40px 40px;"> | |
<p>If you haven&#x27;t heard, | |
<a href="https://github.com/kentcdodds/babel-plugin-macros"><code>babel-plugin-macros</code></a> | |
&quot;enables zero-config, importable babel plugins.&quot; A few months ago, I published a | |
blog post about it on the official babel blog: | |
<a href="https://babeljs.io/blog/2017/09/11/zero-config-with-babel-macros">&quot;Zero-config code transformation with babel-plugin-macros&quot;</a>.</p><p>Since then, there have been a few exciting developments:</p><ol><li><strong>You can use it with a create-react-app application</strong> (v2 beta) because | |
it&#x27;s now included by default in the beta version of | |
<a href="https://www.npmjs.com/package/babel-preset-react-app"><code>babel-preset-react-app</code></a> | |
(which is what create-react-app v2 beta is using!)</li><li>It <a href="https://github.com/fkling/astexplorer/pull/303">was added</a> as an | |
optional transform to <a href="https://astexplorer.net">astexplorer.net</a> by | |
<a href="https://twitter.com/FWeinb">@FWeinb</a></li></ol><p>Up until now, only early adopters have tried to | |
<a href="https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/author.md">write a macro</a>, | |
though there are a fair amount of | |
<a href="https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md">people using</a> | |
the growing list of | |
<a href="https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/macros.md">existing macros</a>. | |
There are tons of awesome things you can do with <code>babel-plugin-macros</code>, and I | |
want to dedicate this newsletter to showing you how to get started playing | |
around with writing your own.</p><p>Let&#x27;s start off with a contrived macro that can split a string of text and | |
replace every space with <code>🐶</code>. We&#x27;ll call it <code>gemmafy</code> because my dog&#x27;s name is | |
&quot;Gemma.&quot; Woof!</p><ol><li>Go to <a href="https://astexplorer.net">astexplorer.net</a></li><li>Make sure the language is set to <code>JavaScript</code></li><li>Make sure the parser is set to <code>babylon7</code></li><li>Enable the transform and set it to <code>babel-macros</code> (or <code>babel-plugin-macros</code> | |
as soon as <a href="https://github.com/fkling/astexplorer/pull/318">this is merged</a>)</li></ol><p>Then copy/paste this in the source (top left) code panel:</p><pre><code class="language-js">import gemmafy from &#x27;gemmafy.macro&#x27; | |
console.log(gemmafy(&#x27;hello world&#x27;)) | |
</code></pre><p>And copy/paste this in the transform (bottom left) code panel:</p><pre><code class="language-js">module.exports = createMacro(gemmafyMacro) | |
function gemmafyMacro({references, state, babel}) { | |
references.default.forEach(referencePath =&gt; { | |
const [firstArgumentPath] = referencePath.parentPath.get(&#x27;arguments&#x27;) | |
const stringValue = firstArgumentPath.node.value | |
const gemmafied = stringValue.split(&#x27; &#x27;).join(&#x27; 🐶 &#x27;) | |
const gemmafyFunctionCallPath = firstArgumentPath.parentPath | |
const gemmafiedStringLiteralNode = babel.types.stringLiteral(gemmafied) | |
gemmafyFunctionCallPath.replaceWith(gemmafiedStringLiteralNode) | |
}) | |
} | |
</code></pre><blockquote><p><em>Alternatively, you can | |
<a href="https://astexplorer.net/#/gist/9d287441b6bd345f9e113c9c3b2b2aee/d5ebca867a522f8aa0120643883b97b83ee23fb4">open this</a></em></p></blockquote><p>TADA 🎉! You&#x27;ve written your (probably) very first babel plugin via a macro!</p><p>Here&#x27;s the output that you should be seeing (in the bottom right panel):</p><pre><code class="language-js">console.log(&#x27;hello 🐶 world&#x27;) | |
</code></pre><p>You&#x27;ll notice that <code>babel-plugin-macros</code> will take care of removing the import | |
at the top of the file for you, and our macro replaced the <code>gemmafy</code> call with | |
the string.</p><p>So here&#x27;s your challenge. Try to add this:</p><pre><code class="language-js">console.log(gemmafy(&#x27;hello world&#x27;, &#x27;world goodbye&#x27;)) | |
</code></pre><p>Right now that&#x27;ll transpile to:</p><pre><code class="language-js">console.log(&#x27;hello 🐶 world&#x27;) | |
</code></pre><p>Your job is to make it do this instead:</p><pre><code class="language-js">console.log(&#x27;hello 🐶 world&#x27;, &#x27;goodbye 🐶 world&#x27;) | |
</code></pre><p>From there, you can play around with it and do a lot of fun things!</p><p>If you want to see more of the capabilities, then copy this in the source (top | |
left):</p><pre><code class="language-jsx">import myMacro, {JSXMacro} from &#x27;AnyNameThatEndsIn.macro&#x27; | |
// (note: in reality, the AnyNameThatEndsIn.macro should be the name of your package | |
// for example: `codegen.macro`) | |
const functionCall = myMacro(&#x27;Awesome&#x27;) | |
const jsx = &lt;JSXMacro cool=&quot;right!?&quot;&gt;Hi!&lt;/JSXMacro&gt; | |
const templateLiteral = myMacro`hi ${&#x27;there&#x27;}` | |
literallyAnythingWorks(myMacro) | |
</code></pre><p>And copy/paste this in the transform (bottom left) code panel:</p><pre><code class="language-js">module.exports = createMacro(myMacro) | |
function myMacro({references, state, babel}) { | |
// `state` is the second argument you&#x27;re passed to a visitor in a | |
// normal babel plugin. `babel` is the `@babel/core` module. | |
// do whatever you like to the AST paths you find in `references`. | |
// open up the console to see what&#x27;s logged and start playing around! | |
// references.default refers to the default import (`myMacro` above) | |
// references.JSXMacro refers to the named import of `JSXMacro` | |
const {JSXMacro = [], default: defaultImport = []} = references | |
defaultImport.forEach(referencePath =&gt; { | |
if (referencePath.parentPath.type === &#x27;TaggedTemplateExpression&#x27;) { | |
console.log( | |
&#x27;template literal contents&#x27;, | |
referencePath.parentPath.get(&#x27;quasi&#x27;), | |
) | |
} else if (referencePath.parentPath.type === &#x27;CallExpression&#x27;) { | |
if (referencePath === referencePath.parentPath.get(&#x27;callee&#x27;)) { | |
console.log( | |
&#x27;function call arguments (as callee)&#x27;, | |
referencePath.parentPath.get(&#x27;arguments&#x27;), | |
) | |
} else if ( | |
referencePath.parentPath.get(&#x27;arguments&#x27;).includes(referencePath) | |
) { | |
console.log( | |
&#x27;function call arguments (as argument)&#x27;, | |
referencePath.parentPath.get(&#x27;arguments&#x27;), | |
) | |
} | |
} else { | |
// throw a helpful error message or something :) | |
} | |
}) | |
JSXMacro.forEach(referencePath =&gt; { | |
if (referencePath.parentPath.type === &#x27;JSXOpeningElement&#x27;) { | |
console.log(&#x27;jsx props&#x27;, { | |
attributes: referencePath.parentPath.get(&#x27;attributes&#x27;), | |
children: referencePath.parentPath.parentPath.get(&#x27;children&#x27;), | |
}) | |
} else { | |
// throw a helpful error message or something :) | |
} | |
}) | |
} | |
</code></pre><p>Next, open up your developer console and check out the console logs. Have fun | |
with that!</p><blockquote><p><em>Alternatively, you can just | |
<a href="https://astexplorer.net/#/gist/6efcadfda8975787d515a4a37c1a600a/635ba8b54af89d52171739c43a9a8a41627d461a">go here</a></em></p></blockquote><h3>Conclusion</h3><p>I think there are a LOT of really cool places we can go with this technology. I | |
didn&#x27;t spend any time in this newsletter talking about the <em>why</em> behind macros | |
or giving you ideas. I&#x27;ll link to some resources for ideas below. The basic idea | |
is if there&#x27;s a way that you can pre-compile some of your operations, then you | |
can improve runtime performance/bundle size of your application. In addition, | |
this allows you to do some things at build time when you have access to the file | |
system. The possibilities are really endless and we&#x27;re just getting started! | |
Enjoy!</p><p><strong>Learn more about ASTs from me</strong>:</p><ul><li><a href="https://youtu.be/nlAHtAQlFGk?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">All about macros with babel-plugin-macros 🎣 (talk at ReactJS Utah)</a></li><li><a href="https://frontendmasters.com/workshops/code-transformation-linting-asts">Code Transformation and Linting Course on Frontend Masters</a></li><li><a href="https://youtu.be/-iA7TAUGn2Y?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Code Transformation and Linting Workshop (very rough practice run)</a></li><li><a href="https://youtu.be/VBscbcm2Mok?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf">Writing custom Babel and ESLint plugins with ASTs (talk at Open West 2017)</a></li></ul><p><strong>Things to not miss</strong>:</p><ul><li><a href="https://youtube.com/watch?list=PLCC436JpVnK3xH_ArpIjdkYDGwWNkVa73&amp;v=aOWIJ4Mgb2k">React Europe Talks Day 1</a></li><li><a href="https://youtu.be/WYWVGQKnz5M?list=PLCC436JpVnK1X7atG6EIz467Evs4TMX_5">React Europe Talks Day 2</a></li><li><a href="https://youtu.be/WYWVGQKnz5M">Stop writing code — Sunil Pai aka @threepointone at @ReactEurope 2018</a> — See | |
the part where <a href="https://twitter.com/threepointone">Sunil</a> talks about the | |
origin story of <code>babel-plugin-macros</code><a href="https://youtu.be/WYWVGQKnz5M?t=8m57s">starting at 8m57s</a>. (Also, I love you | |
too Sunil 😍)</li><li><a href="https://youtu.be/NhmrbpVKgdQ?list=LLz-BYvuntVRt_VpfR6FKXJw">Pre-evaluate code at build time</a> | |
from <a href="http |
View raw
(Sorry about that, but we can’t show files that are this big right now.)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment