<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vladimir Klepov</title>
    <description>The latest articles on Forem by Vladimir Klepov (@thoughtspile).</description>
    <link>https://forem.com/thoughtspile</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F609052%2F44b7db6e-61a2-490d-88c3-b9792e50a239.jpeg</url>
      <title>Forem: Vladimir Klepov</title>
      <link>https://forem.com/thoughtspile</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thoughtspile"/>
    <language>en</language>
    <item>
      <title>I conducted 60 interviews in 2 months — here's what I learned</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Sat, 06 Jan 2024 00:00:00 +0000</pubDate>
      <link>https://forem.com/thoughtspile/i-conducted-60-interviews-in-2-months-heres-what-i-learned-a41</link>
      <guid>https://forem.com/thoughtspile/i-conducted-60-interviews-in-2-months-heres-what-i-learned-a41</guid>
      <description>&lt;p&gt;It's hard to believe, but, starting mid-october 2023 I conducted 60 technical interviews and hired 10 people into our team. It's been &lt;em&gt;extremely&lt;/em&gt; tiring: around 80 hours of &lt;em&gt;active&lt;/em&gt; interviewing, plus writing interview reports, plus screening CVs and take-home assignments, plus onboarding new members — all while doing my normal work stuff. Still, I feel like I learnt a lot in the process — things that would help me as a candidate in the future, and might help you land your next job.&lt;/p&gt;

&lt;p&gt;Note that I'm a fairly relaxed interviewer, and, as an internal startup of a large tech company, we generally have a more &lt;em&gt;humane&lt;/em&gt; hiring process, so your mileage may vary. Still, I've done my best to pick the tips that I feel are universally applicable.&lt;/p&gt;

&lt;p&gt;Here are nine insights I took out of this experience, in no particular order:&lt;/p&gt;

&lt;h2&gt;
  
  
  Be generous with your "expected income".
&lt;/h2&gt;

&lt;p&gt;Say you're a solid higher-middle engineer, and you ask for a senior salary. My thought process: OK buddy, it's a bit more than reasonable &lt;em&gt;now,&lt;/em&gt; but I won't have to fight for your promotion 8–12 months from now when you get there, and I don't have to spend another 12 hours of my own time (and leave my team understaffed for another few weeks) looking for a real hardcore senior, so I'll let you have it. Now suppose you ask for a junior salary. It's suspicious — &lt;em&gt;why is your bar so low?&lt;/em&gt; Is there someting about your work performance you're not telling us? So, do your research on reasonable salaries for your level of experience, and aim &lt;em&gt;slightly&lt;/em&gt; above that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask the right questions.
&lt;/h2&gt;

&lt;p&gt;I always leave time for the candidate to ask &lt;em&gt;me&lt;/em&gt; questions — obviously, this lets the candidate probe what it's like to work at our team, but it's also the best opportunity for me to learn what really matters to the candidate. I've never been much of an asker myself, but now I see that "Thanks, I have no questions" does not look good — if anything, it paints you as someone who doesn't care. Here's a short list of good questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What does the daily work in this role look like? Harmless.&lt;/li&gt;
&lt;li&gt;What features are you building next? Caring about the overall product, nice. Sometimes the answer is "I can't disclose this secret", but not that often.&lt;/li&gt;
&lt;li&gt;Anyting about processes or team structure: how many people are on the team? How often do you release? What regular meetings do you have? Interested in organization, might want to be a team lead someday, great.&lt;/li&gt;
&lt;li&gt;Anything tech-related: which framework do you use? Why did you pick framework X? How do you test your app? Especially suitable for junior- to middle developers who are most involved in hands-on work.&lt;/li&gt;
&lt;li&gt;What kind of tasks do you see me doing? Again, just a good neutral question, because responsibilities for any role differ wildly between companies.&lt;/li&gt;
&lt;li&gt;What growth / promotion opportunities does this position have? Cool trick, flipping the feared "where do you see yourself in 5 years" question against the hiring manager.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here are a few questions that are &lt;em&gt;not&lt;/em&gt; very good:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do you use jira and github? It's a minor detail, won't you be able to work with youtrack and gitlab?&lt;/li&gt;
&lt;li&gt;Do you sometimes work late? Only if something breaks, but overall this question makes you seem a bit &lt;em&gt;lazy.&lt;/em&gt; People on poor teams that routinely overtime aren't likely to answer this question honestly, at any rate.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Social skills matter.
&lt;/h2&gt;

&lt;p&gt;I understand that not everybody is super outgoing, but if we already feel awkward 1 hour into our acquaintance, why work together — to feel awkward for months to come? Just a few tips anyone can follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be energetic. You're tired, I'm tired, we're all tired of endless interviews. Are you just tired today, or generally always too tired to get anything done? I know it's easier said than done, but try and show me all the energy you have left.&lt;/li&gt;
&lt;li&gt;Show respect. People enjoy being respected. Very easy one: &lt;em&gt;you have a great product. Sounds like you have a great engineering culture. This is one of the most interesting interviews I've ever seen.&lt;/em&gt; Like, I know you don't necessarily mean that, but subconsciously I'm very pleased: "oh yes, I'm very proud of my interview process, thanks for noticing"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a related note...&lt;/p&gt;

&lt;h2&gt;
  
  
  Provide conversation opportunities.
&lt;/h2&gt;

&lt;p&gt;Q: Do you use TDD? Bad answer: "no". Good answer: "no, but I've heard of it. Interesting approach. Does your team use TDD?" Now you get to spend 5 minutes talking on your terms instead of being bombarded with random questions, &lt;em&gt;and&lt;/em&gt; you come off as someone curious about stuff.&lt;/p&gt;

&lt;p&gt;On another related note...&lt;/p&gt;

&lt;h2&gt;
  
  
  It's easy to hurt people.
&lt;/h2&gt;

&lt;p&gt;People normally ask you about stuff because they care about it. So, again, the interviewer askning "do you use TDD?", presumably, likes TDD and uses it. So, the worst answer: "no, TDD sucks, it's pure waste of time for idiots." A &lt;em&gt;rare&lt;/em&gt; interviewer might appreciate you having a strong opinion on a topic, but to most this just paints you as a jerk, kinda like &lt;em&gt;"Here's a photo of my children — "I hate children, and yours are especialy horrible".&lt;/em&gt; Not smart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smart talk is not your friend.
&lt;/h2&gt;

&lt;p&gt;Saying stuff like "our front-end guild evaluated several cutting-edge approaches to testing universal applications" only makes you seem smart &lt;em&gt;if&lt;/em&gt; you can elaborate on that topic: &lt;em&gt;what&lt;/em&gt; these approaches were, the pros and cons you found, what tradeoffs you made for your final decision. If you can't answer a follow-up question beside "we settled on jest, not sure why", it was better to stay away from that topic altogether. Related: "in code reviews, I always consider the optimality of the algorithm selected" (proceeds to estimate the time complexity of comparison-based sorting as O(1). I never ask this unless the candidate boasts about her algo skillz).&lt;/p&gt;

&lt;h2&gt;
  
  
  Admit your mistakes.
&lt;/h2&gt;

&lt;p&gt;Don't know an answer? Your code has a bug? It's always better to &lt;em&gt;admit it&lt;/em&gt; and then try to come up with something at the spot than trying to talk your way out of it. &lt;em&gt;Event loop? Sure thing, I'm an expert on loops. It's the way events are looped. Uses logarithmic weighing.&lt;/em&gt; Again, this makes you look like a candidate with big mouth and small hands. I &lt;em&gt;have&lt;/em&gt; seen a couple of people who could talk their way out of any situation, but I honestly think with such skills you'd do better in a different line of work, like international relations, or selling financial services. Note that you really &lt;em&gt;should&lt;/em&gt; give it your best shot — giving up at the first sign of trouble is not a good impression. If you genuinely have no idea — see &lt;em&gt;conversation opportunity:&lt;/em&gt; "Event delegation? Tough luck, never heard of it. &lt;em&gt;Would you tell me about it so that I learn something new today?&lt;/em&gt;"&lt;/p&gt;

&lt;h2&gt;
  
  
  Make yourself memorable.
&lt;/h2&gt;

&lt;p&gt;It's hard to keep detailed profiles of 10 candidates in mind — after a good interview streak all I remember is the general impression (great / OK / horrible) and a &lt;em&gt;few&lt;/em&gt; truly notable things. This guy worked for some crypto scam that went bust, that girl had a cute dog that was trying to eat the camera. The worst you can do is be a totally &lt;em&gt;neutral&lt;/em&gt; candidate — we've had an interview, but I can't remember any details. So try and sneak some anecdote, or wear a silly scarf — &lt;em&gt;something&lt;/em&gt; to remember. This point is especially important for intern / junior positions — online JS bootcamps do a good job of covering the basics, and it's really hard to differentiate these candidates. The memorable thing doesn't have to be professional, or even positive (even though it sure won't hurt) — your best bet would be some &lt;em&gt;original&lt;/em&gt; personal project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask for feedback on the spot.
&lt;/h2&gt;

&lt;p&gt;Asking how you did at the end of the interview doesn't hurt. Yes, some interviewers will be hesitant to answer — at large companies, the feedback is normally sent through the recruiter, and you're never sure if sidestepping this process would get you into trouble. Besides, if the feedback is not complimentary, you're essentially asking for conflict at the spot, and people normally avoid conflict when possible. Still, it's a chance to adjust your expectations (if the interviewer says, looking you in the eyes, that you've done great, it's a good sign), and you &lt;em&gt;might&lt;/em&gt; get actually useful tips that would probably get lost passing through the written report, and then through the non-technical recruiter.&lt;/p&gt;

</description>
      <category>career</category>
      <category>softskills</category>
      <category>programming</category>
    </item>
    <item>
      <title>I built Hippotable for in-browser data analysis</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Wed, 27 Dec 2023 19:41:48 +0000</pubDate>
      <link>https://forem.com/thoughtspile/i-built-hippotable-for-in-browser-data-analysis-4o4h</link>
      <guid>https://forem.com/thoughtspile/i-built-hippotable-for-in-browser-data-analysis-4o4h</guid>
      <description>&lt;p&gt;I'm happy to announce the first public release of &lt;a href="https://blog.thoughtspile.tech/hippotable/" rel="noopener noreferrer"&gt;Hippotable&lt;/a&gt; — a tool that lets you analyze data without leaving your browser, on desktop &amp;amp; mobile.&lt;/p&gt;

&lt;p&gt;I often analyze small- to mid-sized datasets for work and for fun — e.g. to find out the distribution of a certain bug by platform, or calculate unique affected users. But what tools do I have to help me here?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Bash&lt;/strong&gt; lets you &lt;code&gt;uniq | wc -l&lt;/code&gt; — handy, but making advanced pipelines is hard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google sheets&lt;/strong&gt; does the job, but struggles above 10K rows due to all the cruft, and using it for sensitive data such as personal budgets or user data is a no-no.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python + jupyter + pandas&lt;/strong&gt; is up to any data problem, but it's overkill for my simplistic use cases, and requires a lot of code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I set out to build a simple browser-based tool to do the job. Hippotable can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open CSV files up to 100 Mb in size.&lt;/li&gt;
&lt;li&gt;Scroll though thousands of rows.&lt;/li&gt;
&lt;li&gt;Filter and sort your data in real time.&lt;/li&gt;
&lt;li&gt;Aggregate / groupby data to gain deeper insights.&lt;/li&gt;
&lt;li&gt;🏗️ Build powerful data pipelines with multiple filter / aggregate steps.&lt;/li&gt;
&lt;li&gt;Share results with CSV export.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's also free and open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Now, let me walk you through an example of analyzing an annotated movie dataset from &lt;a href="https://www.kaggle.com/datasets/stefanoleone992/filmtv-movies-dataset" rel="noopener noreferrer"&gt;kaggle.&lt;/a&gt; Let's start simple and see which countries, on average, make the best movies. Group by country, sort by average rating:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhck0ngmgvobmjkbudgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhck0ngmgvobmjkbudgi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hm, this looks like a selection of countries which happened to co-produce a decent film &lt;em&gt;once,&lt;/em&gt; not that interesting. Let's try again, removing countries that have &amp;lt;10 movies:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gekufs02ckoi1mir60c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gekufs02ckoi1mir60c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that's unexpected! In case you're curious, lots and lots of bad films come from Italy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpyuvp9fv9mb7jszs37je.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpyuvp9fv9mb7jszs37je.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Combining multiple filter and aggregation layers enables really powerful processing pipelines. For example, here are countries that were home to most great directors (see, not all is lost for Italy):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31w7awzwsl9rnxnyxs74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31w7awzwsl9rnxnyxs74.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That's it for today! &lt;a href="https://blog.thoughtspile.tech/hippotable/" rel="noopener noreferrer"&gt;Give hippotable a try&lt;/a&gt; and &lt;a href="https://github.com/thoughtspile/hippotable" rel="noopener noreferrer"&gt;star on GitHub&lt;/a&gt; to help spread the word. Join me next time to learn about the amazing tech I used to make this happen.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>datascience</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Meet banditypes — the smallest TypeScript validation library</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Wed, 08 Mar 2023 21:04:35 +0000</pubDate>
      <link>https://forem.com/thoughtspile/meet-banditypes-the-smallest-typescript-validation-library-58go</link>
      <guid>https://forem.com/thoughtspile/meet-banditypes-the-smallest-typescript-validation-library-58go</guid>
      <description>&lt;p&gt;I'm excited to announce  &lt;a href="https://github.com/thoughtspile/banditypes"&gt;banditypes&lt;/a&gt; — a new validation library for TS and JS. You may have heard of &lt;a href="https://zod.dev/"&gt;zod&lt;/a&gt; or &lt;a href="https://github.com/ianstormtaylor/superstruct"&gt;superstruct&lt;/a&gt; — these great tools let you define a JS schema to validate objects, and automatically convert the schema to a TS type to avoid duplication. Well, banditypes does the same thing, but in 400 bytes!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EaHVwAy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.thoughtspile.tech/images/banditypes-sample.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EaHVwAy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.thoughtspile.tech/images/banditypes-sample.png" alt="A code sample of banditypes validator" width="880" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Curious to learn how it can be so small? It boils down to three design principles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Strict scope cutting. More full-featured validation libraries tell you &lt;em&gt;what,&lt;/em&gt; exactly, failed validation. This feature had to go.&lt;/li&gt;
&lt;li&gt;Export functions, not objects with methods, because functions tree-shake better. This is exactly why superstruct is already 3–5 times lighter than zod.&lt;/li&gt;
&lt;li&gt;More extensibiltiy. If you cut features from the core, the users must be able to write the missing bits themselves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And some more optimization on top:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compile to modern JS. Gzip is good, but replacing &lt;code&gt;function ()&lt;/code&gt; with &lt;code&gt;() =&amp;gt;&lt;/code&gt; still saved 20 bytes!&lt;/li&gt;
&lt;li&gt;Minimal API. Bye-bye &lt;code&gt;literal(42)&lt;/code&gt;, we have &lt;code&gt;enums([42])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Gzip-aware code style. Gzip removes repetition, so you need to make different parts of code as similar as possible. Write &lt;code&gt;Object&lt;/code&gt; once, repeat for free. Surprise: copy-pasting functions with minor changes is often smaller under gzip than &lt;em&gt;proper&lt;/em&gt; code reuse.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have published an &lt;a href="https://blog.thoughtspile.tech/2023/03/02/banditypes-smallest-validation/"&gt;article&lt;/a&gt; with a more in-depth explanation. &lt;/p&gt;




&lt;p&gt;Banditypes is not a validation library for every use case, but it gains something from its minimal design, and was fun to build. Check it out &lt;a href="https://github.com/thoughtspile/banditypes"&gt;on GitHub,&lt;/a&gt; and maybe even give it a spin in your next project!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I ditched Google analytics and went self-hosted</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Sun, 12 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/thoughtspile/i-ditched-google-analytics-and-went-self-hosted-2nj4</link>
      <guid>https://forem.com/thoughtspile/i-ditched-google-analytics-and-went-self-hosted-2nj4</guid>
      <description>&lt;p&gt;I love writing, and I also love &lt;em&gt;data.&lt;/em&gt; When starting my blog, I integrated Google Analytics — it's free, easy to set up (just drop a few tags on the page), and that's what I knew back then. I did not &lt;em&gt;enjoy&lt;/em&gt; it being run by a big corporation, but I was too lazy to research the alternatives, and then pay for them. But when Google announced &lt;a href="https://support.google.com/analytics/answer/11583528"&gt;sunsetting the current generation of analytics,&lt;/a&gt; and I had to take some action at any rate, I decided to pull the trigger and make a jump to an open-source alternative.&lt;/p&gt;

&lt;p&gt;My analytics needs are quite simple, really:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See how many people read my articles, along with basic stats like session depth and duraiton, because this makes me feel good.&lt;/li&gt;
&lt;li&gt;Filter by source to see which places work better for sharing my content.&lt;/li&gt;
&lt;li&gt;Custom events, so that I see how design changes help me grow an audience (&lt;em&gt;and&lt;/em&gt; this works as a cheap JS error tracker).&lt;/li&gt;
&lt;li&gt;Data export would be nice — sometimes I play around with it in pandas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I give compelling reasons to switch to self-hosted analytics. I share my research on seven open-source analytics tools so that you don't have to do it yourself. Then, I provide a quick tutorial to get up and running with &lt;a href="https://plausible.io/"&gt;plausible.&lt;/a&gt; Whether you're still on google analytics (or a comparable corporate tool like &lt;a href="https://www.facebook.com/business/tools/meta-pixel"&gt;Meta pixel&lt;/a&gt; or &lt;a href="https://metrica.yandex.com/about?"&gt;Yandex metrica&lt;/a&gt;), or just considering adding analytics to your website, this will get you up and running in no time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why self-host your web analytics
&lt;/h2&gt;

&lt;p&gt;I know that paying (either by subscribing to a paid hosted version or maintaining your own infrastructure) for something you can get for free needs justification. And in case on web analytics, you do get decent benefits for your buck:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy — we all know google eats user data for breakfast, lunch and dinner. You "pay" for google analytics with your users' data. The tools we're discussing do &lt;em&gt;not&lt;/em&gt; come from companies that run the internet, and don't have the same data collection motivation, so you do protect your user privacy by switching. You &lt;em&gt;might&lt;/em&gt; argue "I like programming" is not the most private information, so let's move on to the less &lt;em&gt;moral&lt;/em&gt; and more &lt;em&gt;practical&lt;/em&gt; arguments.&lt;/li&gt;
&lt;li&gt;Better data. Google analytics are blocked by most adblock tools, leaving you with incomplete data — &lt;a href="https://towardsdatascience.com/how-much-data-is-missing-from-your-google-analytics-dashboard-20506b26e6d"&gt;some research&lt;/a&gt; suggesting figures around 43%. Switching to a less intrusive alternative gives you back the missing data, and more data means better insights with less effort.&lt;/li&gt;
&lt;li&gt;Fuck big tech. My blog was basically excluded from google search in october 2022 for no reason, and I want revenge. Depriving google of my users' data is like a microbe bite, but it does feel good.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While not directly related to self-hosting or open source, most of the tools we discuss have extra desirable treats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No cookies, so I don't have to worry about GDPR banners or whatever (even though &lt;a href="https://github.com/plausible/analytics/discussions/1963"&gt;some disagree&lt;/a&gt;). To be fair, I'm not 100% sure you absolutely need a banner whenever you have any cookie, but I'm not a lawyer and no cookies sounds like no problems to me.&lt;/li&gt;
&lt;li&gt;Simpler interface. This &lt;em&gt;does&lt;/em&gt; sound like a made-up justification for limited functionality, but I honestly see this as a win for a casual analytics user like myself.&lt;/li&gt;
&lt;li&gt;Smaller JS footprint = better website performance. Google Analytics is famously around 50KB / 21KB gzip of JS, while most tools we discuss have scripts under 2KB. It's not a &lt;em&gt;huge&lt;/em&gt; deal, as a tracking script does not block anything and is cached, but a pleasant bonus. In theory, this lets you catch a few events that occurr soon after the first page load.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, is self-hosting the anallytics worth the trouble of setting up and maintaing the infrastructure? I think the answer is yes, and here's why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yet more privacy. When sending your user data to &lt;em&gt;any&lt;/em&gt; third party, you're counting on their good will. If your data goes to your own server, it just stays there. If you're extra paranoid, you can ban outgoing traffic from the analytics container, or implement any other security measures.&lt;/li&gt;
&lt;li&gt;Yet better data collection. Some privacy protection tools do restrict connections to well-known analytics hosts. Sending data to your own domain makes blocking your tracker much harder, because a domain blacklist won't cut it any more.&lt;/li&gt;
&lt;li&gt;Price. As we'll see, you can self-host analytics for around 6$ a month, which is cheaper than the paid analytics services I've seen, and this price will &lt;em&gt;not&lt;/em&gt; auto-escalate if you suddenly have a traffic spike.&lt;/li&gt;
&lt;li&gt;Data ownership. Your historic data lives on your server, and you're free to export it regardless of any decisions the maintainers of the tools make. Not &lt;em&gt;every&lt;/em&gt; tool on our list has easy export, but nothing stops you from a DB dump in the worst case.&lt;/li&gt;
&lt;li&gt;No vendor lock-in. With your data available, you can (in theory) migrate to any other analytics service if you change your mind. Again, not all the tools can &lt;em&gt;import&lt;/em&gt; data, but some can.&lt;/li&gt;
&lt;li&gt;Future-proof. The company developing your tool of choice goes out of business, switches to closed-source, or makes a big update that requires you to reconfigure everyting from scratch? No worries, you can just stay on your current version as long as you please.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should be enough to convince even the most skeptical readers that self-hosted analytics instance is worth it. Now, what tools are available for the job, and which one should you pick?&lt;/p&gt;

&lt;h2&gt;
  
  
  Open-source web analytics tools, an overview
&lt;/h2&gt;

&lt;p&gt;When it comes to open-source analytics tools, we have three top-tier choices: matomo, plausible and umami.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://matomo.org/"&gt;Matomo&lt;/a&gt; is the most established tool in the game (been doing it since 2011). I have personally seen it used by huge compaines, and people were pleased with it (it was called piwik back then). It's the closest alternative to google analytics in terms of features — you get customizable dashboards, integrations (ecommerce, website builders, multiple search providers), alerts, plugins, exports and what not. It even has iOS / android SDKs. But the truth is, I don't need all these extra features, I don't want to pay for it (in terms of a larger tracking script and more complex dashboards) and I'd be perfectly happy with something simpler, which brings us to...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://plausible.io/"&gt;Plausible&lt;/a&gt;, in contrast, offers a stripped-down analytics experience with only the essential metrics — page views, bounce. duration, source, device, location by default. It's cookie-free and features a tiny script (claims to be "&amp;lt; 1KB", but it's 1.6KB on my page). Any custom events can be sent via JS API. The docs are top notch, the docker set-up is straightforward, and you can import and export CSV data as you please. I'm loving the feature set, and this is what I ultimately went with. One valid criticism of plausible is that it's slightly convoluted, featuring two databases and an elixir server, which makes it pretty hard to set up without docker, but I don't mind it.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://umami.is/"&gt;Umami&lt;/a&gt; is another "simple analytics" tool. I find it &lt;em&gt;extremely&lt;/em&gt; similar to plausible — same minimal dashboard, same cookie-free tracking, same tiny script (1.6KB gzip). It launched in 2020 with great hype, but plausible quickly caught up. As of 2023, umami still lacks &lt;a href="https://github.com/umami-software/umami/discussions/1085"&gt;data import from GA,&lt;/a&gt; and &lt;a href="https://github.com/umami-software/umami/issues/310"&gt;only supports export via DB dump&lt;/a&gt; — plausible can do both easily. I also prefer plausible docs. Umami has only one DB, and &lt;a href="https://www.reddit.com/r/selfhosted/comments/o8r293/comment/h374uqf/?utm_source=share&amp;amp;utm_medium=web2x&amp;amp;context=3"&gt;can run as a raw node service.&lt;/a&gt; Overall, it's a viable choice, and competition is always good.&lt;/p&gt;

&lt;p&gt;For historical reference, &lt;a href="https://usefathom.com/"&gt;fathom&lt;/a&gt; was among the first privacy-focued trackers. It has since re-launched as a paid product, freezing development on the &lt;a href="https://github.com/usefathom/fathom"&gt;open-source version&lt;/a&gt;. The fact that you can still install and use it demonstrates my point on "suture-proofness" in action. I'd prefer something that's actively devloped, and I see no reason to pick it over the tools mentioned above.&lt;/p&gt;

&lt;p&gt;Among the lesser-known tools I found these three to stand out from the competition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.offen.dev/"&gt;Offen&lt;/a&gt; (dope landing page design!) is built around "fairness". Your visitors must give &lt;em&gt;explicit consent&lt;/em&gt; to data collection, and can view or delete their data at any time. On the practical side, it comes with auto-renewing SSL out of the box, which is nice. I'm not against &lt;em&gt;consent,&lt;/em&gt; but I have personally developed &lt;em&gt;popup blindness,&lt;/em&gt; and I'm afraid the users will just scroll with the banner hanging around. Sadly, it can't track custom events (clicks, forms, etc) as of 2023, and has no import / export (presumably by design).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goatcounter.com/"&gt;Goatcounter&lt;/a&gt; can run without JS via a tracking pixel (but comes with full JS version, too). Extras: CSV export, API access, good docs, signle-binary deploy, and the name is quite funny. You might say the UI is dated, but I'd call it &lt;em&gt;hacker-themed.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ackee.electerious.com/"&gt;Ackee&lt;/a&gt; comes with &lt;a href="https://docs.ackee.electerious.com/#/docs/Anonymization"&gt;extra anonymization&lt;/a&gt; and features a &lt;a href="https://docs.ackee.electerious.com/#/docs/API"&gt;GraphQL API&lt;/a&gt; that allows you to import / export data as needed and build custom integrations or dashboards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, of course, you can just take a generic log analyzer (grafana, graylog, goaccess) and throw any data you wish there. You get more flexibility, but there are no precofigured views for common web stats, and you must write the actual tracking logic (both JS and the endpoint) yourself. We track product metrics via a log aggregator at work as it lets us trace problems to back-end issues, but I wouldn't take this path for a simple website.&lt;/p&gt;

&lt;p&gt;Ultimately, I picked plausible because of its minimal approach, complete feature set (mainly data import / export) and great docs. However, all the tools on my list have their strenghts, and you can pick whichver you prefer, or even try a few before commiting. Here's a flowchart with &lt;em&gt;some&lt;/em&gt; things that might influence your decision:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DQPr3oPp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.thoughtspile.tech/images/self-hosted-analytics/alternatives.png%3Finvert" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DQPr3oPp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.thoughtspile.tech/images/self-hosted-analytics/alternatives.png%3Finvert" alt="" width="880" height="1055"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Up and running
&lt;/h2&gt;

&lt;p&gt;Before we get started, you'll most certainly need two pieces of infrastructure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;em&gt;server&lt;/em&gt; — fair enough, you need a hosting to self-host anything. I use an entry-level &lt;a href="https://www.linode.com/pricing/#compute-shared"&gt;linode&lt;/a&gt; for $5 a month, which is a typical price. With your average blog traffic this leaves room for other services — &lt;a href="https://hub.docker.com/r/kylemanna/openvpn"&gt;OpenVPN server,&lt;/a&gt; &lt;a href="https://docs.gitea.io/en-us/"&gt;Gitea&lt;/a&gt; or whatever you wish.&lt;/li&gt;
&lt;li&gt;A custom &lt;em&gt;domain name.&lt;/em&gt; GitHub can't proxy &lt;em&gt;some&lt;/em&gt; traffic from &lt;code&gt;username.github.io&lt;/code&gt; to your analytics server, and HTTPS won't work on a raw server IP without a domain name. You can get one starting at $5 a year (more for a decent TLD, make it $20). I heard &lt;a href="https://porkbun.com/"&gt;porkbun&lt;/a&gt; suggested. You are not obliged to use this domain for your website (even though &lt;a href="https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site"&gt;GH pages support that&lt;/a&gt;), so something shady like &lt;code&gt;fk12sdj1244.cyou&lt;/code&gt; works if you wish.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With that in place, we can set up our plausible instance. I'll make it quick, since there already is a &lt;a href="https://esc.sh/blog/plausible-analytics-selfhosting/"&gt;tutorial by Mansoor,&lt;/a&gt; and the docs do a decent job:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/install/ubuntu/"&gt;Install docker.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Start plausible: clone the repo, edit config, and &lt;code&gt;docker-compose up -d&lt;/code&gt; the service (see &lt;a href="https://plausible.io/docs/self-hosting"&gt;docs&lt;/a&gt;). If this worked, you can access the UI at &lt;code&gt;http://your.ip.1.1:8000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You can't send analytics from your HTTPS website to an HTTP endpoint, so there's more work.&lt;/li&gt;
&lt;li&gt;Go to your DNS settings, come up with an analytics subdoiain (like &lt;code&gt;an.you.io&lt;/code&gt;) and add server IP to the relevant A/AAAA record. Plausible &lt;em&gt;can not&lt;/em&gt; live on a &lt;code&gt;/path&lt;/code&gt;, it needs its own subdomain. Success = the UI is live on &lt;code&gt;http://an.you.io:8000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now we need a reverse proxy for SSL termination. I went for a full-dockerized setup with &lt;a href="https://github.com/nginx-proxy/nginx-proxy"&gt;nginx-proxy&lt;/a&gt; container. Set &lt;code&gt;VIRTUAL_HOST&lt;/code&gt; in &lt;code&gt;plausible-conf&lt;/code&gt; to enable discovery, append &lt;code&gt;nginx-proxy&lt;/code&gt; to &lt;code&gt;docker-compose.yml&lt;/code&gt;, restart, and you should be able to access analytics at &lt;code&gt;http://an.you.io&lt;/code&gt;. Un-exposing the port 8000 at this point won't hurt.&lt;/li&gt;
&lt;li&gt;Now, the actual SSL certificates. nginx-proxy has an &lt;a href="https://github.com/nginx-proxy/acme-companion"&gt;acme-companion&lt;/a&gt; container that manages letsencrypt certificates for you. Add it to your &lt;code&gt;docker-compose.yml&lt;/code&gt; along with all the extra volume declaraions, and you're all set — &lt;code&gt;https://an.you.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It's time to add &lt;code&gt;https://an.you.io/js/script.js&lt;/code&gt; script to your website and watch the data flow.&lt;/li&gt;
&lt;li&gt;The most painful step is importing historical data from google analytics (thanks google, it &lt;em&gt;is&lt;/em&gt; possible). Here are &lt;a href="https://plausible.io/docs/google-analytics-import"&gt;the docs&lt;/a&gt; to help you. Took me an hour of Google Cloud and SSO suffering, but it all worked out fine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Et voila, you're self-hosting your analytics. No need to involve google nay more. The same process applies to any dockerized setup with minor adjustments, not just plausible.&lt;/p&gt;




&lt;p&gt;Self-hosting your website analytics comes at a cost — to be precise, around $7 a month for hosting and the domain, but you can use the same infrastructure for other tasks &lt;em&gt;and&lt;/em&gt; you get quite a few benefits in doing so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User privacy — no data sent to any third parties.&lt;/li&gt;
&lt;li&gt;Less missing data — your own domain is unlikely to be adblocked.&lt;/li&gt;
&lt;li&gt;Data ownership — you can always export historic data and move to another tool (worst case — via SQL dump).&lt;/li&gt;
&lt;li&gt;Future-proofness — even if the developer goes out of business, your instance stays with you.&lt;/li&gt;
&lt;li&gt;Price (when compared to hosted alternatives) — you can easily run analytics for under $7/mo, including a custom domain, while hosted solutions start at $9.&lt;/li&gt;
&lt;li&gt;Most importantly, it's one small step that we, as a content creators, can take to make a dent in corporate monopoly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most privacy-focused analytics tools also feature no cookies (aka fewer legal concerns, but not like in legal advice etc.), simpler interface, and smaller tracking JS (both at the expense of less data).&lt;/p&gt;

&lt;p&gt;We examined seven open-source analytics tools that you can self-host. Here they are, in order of my preference:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://plausible.io/"&gt;Plausible&lt;/a&gt; is a minimal analytics solution with &lt;em&gt;enough&lt;/em&gt; features for me: custom events, API, GA import, CSV export. Great docs!&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://umami.is/"&gt;Umami&lt;/a&gt; is very similar to plausible. I prefer import / export and docs on plausible, but umami has slightly better mobile UX. Close call.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goatcounter.com/"&gt;Goatcounter&lt;/a&gt; is a &lt;em&gt;hacker-style&lt;/em&gt; tracker. Runs as a single binary, supports no-JS tracking via pixels or log imports. UI choices are divisive.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://matomo.org/"&gt;Matomo&lt;/a&gt; is a well-established platform with lost and lots of features you probably don't need.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ackee.electerious.com/"&gt;Ackee&lt;/a&gt; comes with a GraphQL API and nice UI.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.offen.dev/"&gt;Offen&lt;/a&gt; is &lt;em&gt;conceptual:&lt;/em&gt; it requires explicit consent, and users can view their data. How well this works in practice remains to be seen. Some features are missing — most importantly, custom event tracking.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://usefathom.com/"&gt;Fathom&lt;/a&gt; is not bad, but I can't recommend it since the development of free version has been shut down.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, I showed you how to install and integrate your own plausible instance, along with HTTPS.&lt;/p&gt;

&lt;p&gt;Now it's your turn to self-host your analytics and say good-bye to google.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>analytics</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Making sense of TypeScript using set theory</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Mon, 23 Jan 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/thoughtspile/making-sense-of-typescript-using-set-theory-lj7</link>
      <guid>https://forem.com/thoughtspile/making-sense-of-typescript-using-set-theory-lj7</guid>
      <description>&lt;p&gt;I've been working with TypeScript for a long long time. I think I'm not too bad at it. However, to my despair, some low-level behaviors still confuse me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why does &lt;code&gt;0 | 1 extends 0 ? true : false&lt;/code&gt; evaluate to &lt;code&gt;false&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;I'm very ashamed, but I sometimes confuse "subtype" and "supertype". Which is which?&lt;/li&gt;
&lt;li&gt;While we're at it, what are type "narrowing" and "widening", and how do they relate to sub/supertypes?&lt;/li&gt;
&lt;li&gt;If you want an object that satisfies both &lt;code&gt;{ name: string }&lt;/code&gt; and &lt;code&gt;{ age: number }&lt;/code&gt;, do you &lt;code&gt;&amp;amp;&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt;? Both make some sense, since I want a &lt;em&gt;union&lt;/em&gt; of the functionality in both interfaces, but I also want the object to satisfy left &amp;amp; (and) right interfaces.&lt;/li&gt;
&lt;li&gt;How is &lt;code&gt;any&lt;/code&gt; different from &lt;code&gt;unknown&lt;/code&gt;? All I get is imprecise mnemonics like "Avoid Any, Use Unknown". Why?&lt;/li&gt;
&lt;li&gt;What, exactly, is &lt;code&gt;never&lt;/code&gt;? "A value that &lt;em&gt;never&lt;/em&gt; happens" is very dramatic, but not too precise.&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;whatever | never === whatever&lt;/code&gt; and &lt;code&gt;whatever &amp;amp; never === never&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Why on earth is &lt;code&gt;const x: {} = true;&lt;/code&gt; valid TS code? &lt;code&gt;true&lt;/code&gt; is clearly not an empty object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was doing some research on &lt;code&gt;never&lt;/code&gt;, and stumbled upon Zhenghao He's &lt;a href="https://www.zhenghao.io/posts/ts-never" rel="noopener noreferrer"&gt;Complete Guide To TypeScript’s Never Type&lt;/a&gt; (check out his blog, it's super cool!). It mentions that a type is just a set of values, and — boom — it clicked. I went back to the basics, re-formulating everything I know about TS into set-theoretic terms. Follow me as I:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refresh my knowledge of set theory,&lt;/li&gt;
&lt;li&gt;Map TS concepts to their set counterparts,&lt;/li&gt;
&lt;li&gt;Start simple with booelan, null and undefined types,&lt;/li&gt;
&lt;li&gt;Extend to strings and numbers, finding some types that TS can not express,&lt;/li&gt;
&lt;li&gt;Jump into objects, proving my assumptions about them wrong,&lt;/li&gt;
&lt;li&gt;Finally gain confidence writing &lt;code&gt;extends&lt;/code&gt; caluses,&lt;/li&gt;
&lt;li&gt;And put &lt;code&gt;unknown&lt;/code&gt; and &lt;code&gt;any&lt;/code&gt; where they belong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, I solve most of my questions, grow much cozier with TS, and come up with this brilliant map of TS types:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Feverything.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Feverything.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set theory
&lt;/h2&gt;

&lt;p&gt;First up, a refresher on set theory. Feel free to skip if you're a pro, but my algebra skills are a bit rusty, so I could use a reminder of how it works.&lt;/p&gt;

&lt;p&gt;Sets are unordered collections of objects. In kindergarten terms: say we have two apples aka &lt;em&gt;objects&lt;/em&gt; (let's call them ivan and bob, shall we?), and some bags aka &lt;em&gt;sets&lt;/em&gt; where we can put the apples. We can make, in total, 4 apple sets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A bag with apple ivan, &lt;code&gt;{ ivan }&lt;/code&gt; — sets are written as curly brackets with the set items inside.&lt;/li&gt;
&lt;li&gt;Similarly, you can have a bag with apple bob, &lt;code&gt;{ bob }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A bag with both apples, &lt;code&gt;{ ivan, bob }&lt;/code&gt;. Hold onto your hats, this is called a &lt;em&gt;universe&lt;/em&gt; because at the moment there's nothing in our world except these two apples.&lt;/li&gt;
&lt;li&gt;An empty bag aka empty set, &lt;code&gt;{}&lt;/code&gt;. This one gets a special symbol, ∅&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sets are often drawn as "venn diagrams", with each set represented as a circle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fapples.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fapples.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apart from listing all the items, we can also build sets by &lt;em&gt;condition.&lt;/em&gt; I can say "R is a set of red apples" to mean &lt;code&gt;{ ivan }&lt;/code&gt;, considernig ivan is red and bob is green. So far, so good.&lt;/p&gt;

&lt;p&gt;Set A is a &lt;em&gt;subset&lt;/em&gt; of set B if every element from A is also in B. In our apple world, &lt;code&gt;{ ivan }&lt;/code&gt; is a subset of &lt;code&gt;{ ivan, bob }&lt;/code&gt;, but &lt;code&gt;{ bob }&lt;/code&gt; is not a subset of &lt;code&gt;{ ivan }&lt;/code&gt;. Obviously, any set is a subset of itself, and &lt;code&gt;{}&lt;/code&gt; is a subset of any other set S, because not a single item from &lt;code&gt;{}&lt;/code&gt; is missing from S.&lt;/p&gt;

&lt;p&gt;There are a few useful operators defined on sets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Union &lt;em&gt;C = A ∪ B&lt;/em&gt; contains all the elements that are in A or in B. Note that &lt;em&gt;A ∪ ∅ = A&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Intersection &lt;em&gt;C = A ∩ B&lt;/em&gt; contains all the elements that are in A &lt;em&gt;and&lt;/em&gt; B. Note that &lt;em&gt;A ∪ ∅ = ∅&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Difference &lt;em&gt;C = A \ B&lt;/em&gt; contains all the elements that are in A, but not in B. Note that &lt;em&gt;A \ ∅ = A&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should be enough! Let's see how it all maps to types.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it have to do with types
&lt;/h2&gt;

&lt;p&gt;So, the big reveal: you can think of "types" as sets of JavaScript values. Then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Our &lt;em&gt;universe&lt;/em&gt; is all the values a JS program can produce.&lt;/li&gt;
&lt;li&gt;A type (not even a typescript type, just a type in general) is some set of JS values.&lt;/li&gt;
&lt;li&gt;Some types can be represented in TS, while other can not — for example, "non-zero numbers".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;A extends B&lt;/code&gt; as seen in &lt;a href="https://www.typescriptlang.org/docs/handbook/2/conditional-types.html" rel="noopener noreferrer"&gt;conditional types&lt;/a&gt; and &lt;a href="https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints" rel="noopener noreferrer"&gt;generic constraints&lt;/a&gt; can be read as "A is subset of B".&lt;/li&gt;
&lt;li&gt;Type union, &lt;code&gt;|&lt;/code&gt;, and intersection, &lt;code&gt;&amp;amp;&lt;/code&gt;, operators are just the union and intersection of two sets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Exclude&amp;lt;A, B&amp;gt;&lt;/code&gt; is as close as TS gets to a difference operator, except it &lt;em&gt;only&lt;/em&gt; works when both &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; are union types.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;never&lt;/code&gt; is an empty set. Proof: &lt;code&gt;A &amp;amp; never = never&lt;/code&gt; and &lt;code&gt;A | never = A&lt;/code&gt; for any type &lt;code&gt;A&lt;/code&gt;, and &lt;code&gt;Exclude&amp;lt;0, 0&amp;gt; = never&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This change of view already yields some useful insights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subtype of type A is a &lt;em&gt;subset&lt;/em&gt; of type A. Supertype is a superset. Easy.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Widening&lt;/em&gt; makes a type-set wider by allowing some extra values. &lt;em&gt;Narrowing&lt;/em&gt; removes certain values. Makes geometrical sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I know this all sounds like a lot, so let's proceed by example, starting with a simple case of boolean values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boolean types
&lt;/h2&gt;

&lt;p&gt;For now, pretend JS only has boolean values. There are exaclty &lt;em&gt;two&lt;/em&gt; — &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;. Recalling the apples, we can make a total of 4 types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Literal types &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;, each made up of a single value;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;boolean&lt;/code&gt;, which is &lt;em&gt;any&lt;/em&gt; boolean value;&lt;/li&gt;
&lt;li&gt;The empty set, &lt;code&gt;never&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The diagram of the "boolean types" is basically the one that we had for apples, just the names swapped:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fbool.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fbool.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's try moving between type world and set world:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;boolean&lt;/code&gt; can be written as &lt;code&gt;true | false&lt;/code&gt; (in fact, that's exactly how TS impements it).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;true&lt;/code&gt; is a subset (aka sub-type) of &lt;code&gt;boolean&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;never&lt;/code&gt; is an empty set, so &lt;code&gt;never&lt;/code&gt; is a sub-set/type of &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;boolean&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;&lt;/code&gt; is an &lt;em&gt;intersection,&lt;/em&gt; so &lt;code&gt;false &amp;amp; true = never&lt;/code&gt;, and &lt;code&gt;boolean &amp;amp; true = (true | false) &amp;amp; true = true&lt;/code&gt; (the universe, &lt;code&gt;boolean&lt;/code&gt;, doesn't affect intersections), and &lt;code&gt;true &amp;amp; never = never&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;|&lt;/code&gt; is a &lt;em&gt;union,&lt;/em&gt; so &lt;code&gt;true | never = true&lt;/code&gt;, and &lt;code&gt;boolean | true = boolean&lt;/code&gt; (the universe, &lt;code&gt;boolean&lt;/code&gt;, "swallows" other intersection items because they're all subsets of universe).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Exclude&lt;/code&gt; correctly computes set difference: &lt;code&gt;Exclude&amp;lt;boolean, true&amp;gt; -&amp;gt; false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, a little self-assessment of the tricky &lt;code&gt;extends&lt;/code&gt; cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you recall that "extends" can be read as "is subset of", the answer should be clear — A0,B1,C1,C1. We're making progress!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; are just like &lt;code&gt;boolean&lt;/code&gt;, except they only contain &lt;em&gt;one&lt;/em&gt; value each. &lt;code&gt;never extends null&lt;/code&gt; still holds, &lt;code&gt;null &amp;amp; boolean&lt;/code&gt; is &lt;code&gt;never&lt;/code&gt; since no JS value can simultaneously be of 2 different JS types, and so on. Let's add these to our "trivial types map":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Ffinites.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Ffinites.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Strings and other primitives
&lt;/h2&gt;

&lt;p&gt;With the simple ones out of the way, let's move on to string types. At first, it seems that nothing's changed — &lt;code&gt;string&lt;/code&gt; is a type for "all JS strings", and every string has a corresponding literal type: &lt;code&gt;const str: 'hi' = 'hi';&lt;/code&gt; However, there's one key difference — there are &lt;em&gt;infinitely many&lt;/em&gt; possible string values.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It might be a lie, because you can only represent so many strings in finite computer memory, but a) it's enough strings to make enumerating them all unpractical, and b) type systems &lt;em&gt;can&lt;/em&gt; operate on pure abstrations without worrying about dirty real-life limitations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just like sets, string types can be constructed in a few different ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;|&lt;/code&gt; union lets you constuct any &lt;em&gt;finite&lt;/em&gt; string set — e.g. &lt;code&gt;type Country = 'de' | 'us';&lt;/code&gt;. This won't work for &lt;em&gt;infinite&lt;/em&gt; sets — say, all strings with length &amp;gt; 2 — since you can't write an infinite list of value.&lt;/li&gt;
&lt;li&gt;Funky &lt;a href="https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html" rel="noopener noreferrer"&gt;template literal types&lt;/a&gt; let you construct &lt;em&gt;some&lt;/em&gt; infinite sets — e.g. &lt;code&gt;type V =&lt;/code&gt;v${string}&lt;code&gt;;&lt;/code&gt; is a set of all strings that start with &lt;code&gt;v&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can go a bit further by making unions and intersections of literal and template types. Fun time: when combining a &lt;em&gt;union&lt;/em&gt; with a &lt;em&gt;template,&lt;/em&gt; TS is smart enough to just filter the literals againts the template, so that &lt;code&gt;'a' | 'b' &amp;amp;&lt;/code&gt;a${string}&lt;code&gt;= 'a'&lt;/code&gt;. Yet, TS is not &lt;em&gt;smart enough&lt;/em&gt; to merge templates, so you get really fancy ways of saying &lt;code&gt;never&lt;/code&gt;, such as &lt;code&gt;a${string}` &amp;amp; `b${string}&lt;/code&gt; (obviously, a string can't start with "a" and "b" at the same time).&lt;/p&gt;

&lt;p&gt;However, some string types are &lt;em&gt;not&lt;/em&gt; representable in TS at all. Try "every string except 'a'". You could &lt;code&gt;Exclude&amp;lt;string, 'a'&amp;gt;&lt;/code&gt;, but since TS doesn't &lt;em&gt;actually&lt;/em&gt; model &lt;code&gt;string&lt;/code&gt; as &lt;em&gt;union of all possible string literals,&lt;/em&gt; this in fact evaluates back to &lt;code&gt;string&lt;/code&gt;. The template grammar can not express this negative condition either. Bad luck!&lt;/p&gt;

&lt;p&gt;The types for numbers, symbols and bigints work the same way, except they don't even get a "template" type, and are limited to finite sets. It's a pity, as I could really use some number subtypes — &lt;em&gt;integer, number between 0 and 1,&lt;/em&gt; or &lt;em&gt;positive number.&lt;/em&gt; Anyways, all together:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fprimitives.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fprimitives.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Phew, we've covered all primitive, &lt;em&gt;non-intersecting&lt;/em&gt; JS / TS types. We've gotten comfortable moving between sets and types, and discovered that some types can't be defined in TS. Here comes the tricky part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interfaces &amp;amp; object types
&lt;/h2&gt;

&lt;p&gt;If you think &lt;code&gt;const x: {} = 9;&lt;/code&gt; makes no sense, this section is for you. As it appears, our mental model of what TS object types / records / interfaces was built on the wrong assumptions.&lt;/p&gt;

&lt;p&gt;First, you'd probably expect types like &lt;code&gt;type Sum9 = { sum: 9 }&lt;/code&gt; to act like "literal types" for objects — matching a single object value &lt;code&gt;{ sum: 9 }&lt;/code&gt;, adjusted for referential equality. This is absolutely &lt;em&gt;not&lt;/em&gt; how it works. Instead, &lt;code&gt;Sum9&lt;/code&gt; is a "&lt;em&gt;thing&lt;/em&gt; on which you can access propery &lt;code&gt;sum&lt;/code&gt; to get &lt;code&gt;9&lt;/code&gt;" — more like a condition / constraint. This lets us call &lt;code&gt;(data: Sum9) =&amp;gt; number&lt;/code&gt; with an object &lt;code&gt;obj = { sum: 9, date: '2022-09-13' }&lt;/code&gt; without TS complaining about unknown &lt;code&gt;date&lt;/code&gt; property. See, handy!&lt;/p&gt;

&lt;p&gt;Then, &lt;code&gt;{}&lt;/code&gt; type is not an "empty object" type corresponding to a &lt;code&gt;{}&lt;/code&gt; JS literal, but a "thing where I can access properties, but I don't care about any particular properties". Aha, now we can see what's going on in our initial mind-bender: if &lt;code&gt;x = 9&lt;/code&gt;, you can safely &lt;code&gt;x['whatever']&lt;/code&gt;, so it satisfies the unconstrained &lt;code&gt;{}&lt;/code&gt; interface. In fact, we can even make bolder claims like &lt;code&gt;const x: { toString(): string } = 9;&lt;/code&gt;, since we can &lt;code&gt;x.toString()&lt;/code&gt; and actuallty get a string. More yet, &lt;code&gt;keyof number&lt;/code&gt; gives us &lt;code&gt;"toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString"&lt;/code&gt;, meaning that TS secretly sees our primitive type as an object, which it is (thanks to &lt;a href="https://javascript.plainenglish.io/javascript-boxing-wrappers-5b5ff9e5f6ab" rel="noopener noreferrer"&gt;autoboxing&lt;/a&gt;). &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; do not satisfy &lt;code&gt;{}&lt;/code&gt;, because they throw if you try to read a property. Not super intuitive, but makes sense now.&lt;/p&gt;

&lt;p&gt;Coming back to my little "&lt;code&gt;|&lt;/code&gt; or &lt;code&gt;&amp;amp;&lt;/code&gt;" problem, &lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;|&lt;/code&gt; operate on "value sets", not on "object shapes", so you need &lt;code&gt;{ name: string } &amp;amp; { age: number }&lt;/code&gt; to get objects with &lt;em&gt;both&lt;/em&gt; &lt;code&gt;name&lt;/code&gt; and (extra hint: and = &lt;code&gt;&amp;amp;&lt;/code&gt;) &lt;code&gt;age&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Oh, and what about that odd &lt;code&gt;object&lt;/code&gt; type? Since every property on an interface just adds a constraint to the "thing" we're typing, there's no way to declare an interface that filters out primitive values. It's why TS has a built-in &lt;code&gt;object&lt;/code&gt; type that means specifically "JS object, not a primitive". Yes, you can intersect with &lt;code&gt;object&lt;/code&gt; to get only &lt;em&gt;non-primitive&lt;/em&gt; values satisfying an interface: &lt;code&gt;const x: object &amp;amp; { toString(): string } = 9&lt;/code&gt; fails.&lt;/p&gt;

&lt;p&gt;Let's add all of these to our type map:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Feverything.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Feverything.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  extends
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;extends&lt;/code&gt; keyword in TS can be confusing. It comes from the object-oriented world where you &lt;em&gt;extend&lt;/em&gt; a class in the sense of &lt;em&gt;adding functionality&lt;/em&gt; to it, &lt;em&gt;but,&lt;/em&gt; since TS uses &lt;a href="https://www.typescriptlang.org/docs/handbook/type-compatibility.html" rel="noopener noreferrer"&gt;structural typing&lt;/a&gt;, &lt;code&gt;extends&lt;/code&gt; as used in &lt;code&gt;type Extends&amp;lt;A, B&amp;gt; = A extends B ? true : false&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; the same &lt;code&gt;extends&lt;/code&gt; from &lt;code&gt;class X extends Y {}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead, &lt;code&gt;A extends B&lt;/code&gt; can be read as &lt;em&gt;A is a sub-type of B&lt;/em&gt; or, in set terms, &lt;em&gt;A is a subset of B.&lt;/em&gt; If B is a union, every member of A must also be in B. If B is a "constrained" interface, A must not violate any of B's constraints. Good news: a usual OOP &lt;code&gt;class A extends B {}&lt;/code&gt; fits &lt;code&gt;A extends B ? 1 : 0&lt;/code&gt;. So does &lt;code&gt;'a' extends string&lt;/code&gt;, meaning that (excuse the pun) TS &lt;code&gt;extends&lt;/code&gt; extends &lt;code&gt;extends&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This "subset" view is the best way to never mix up the order of &lt;code&gt;extends&lt;/code&gt; operands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0 | 1 extends 0&lt;/code&gt; is false, since a 2-element set &lt;code&gt;{0, 1}&lt;/code&gt; is not a subset of the 1-element &lt;code&gt;{0}&lt;/code&gt; (even though &lt;code&gt;{0,1}&lt;/code&gt; does extend &lt;code&gt;{1}&lt;/code&gt; in a geometrical sense).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;never extends T&lt;/code&gt; is always true, because &lt;code&gt;never&lt;/code&gt;, the empty set, is a subset of any set.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;T extends never&lt;/code&gt; is only true if T is &lt;code&gt;never&lt;/code&gt;, because an empty set has no subsets except itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;T extends string&lt;/code&gt; allows T to be a string, a literal, or a literal union, or a template, because all of these are subsets of &lt;code&gt;string&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;T extends string ? string extends T&lt;/code&gt; makes sure that T is &lt;em&gt;exactly&lt;/em&gt; &lt;code&gt;string&lt;/code&gt;, because that's the only way it can be both a subset &lt;em&gt;and&lt;/em&gt; a superset of string.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  unknown and any
&lt;/h2&gt;

&lt;p&gt;Typescript has two types that can represent an arbitrary JS value — &lt;code&gt;unknown&lt;/code&gt; and &lt;code&gt;any&lt;/code&gt;. The normal one is &lt;code&gt;unknown&lt;/code&gt; — the universe of JS values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// It's a 1&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// a shorter one, given the {} oddity&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// For other types, this is 0:&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a puzzling side, though:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;unknown&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; a union of all other base types, so you can't &lt;code&gt;Exclude&amp;lt;unknown, string&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unknown extends string | number | boolean | object | bigint | symbol | null | undefined&lt;/code&gt; is false, meaning that some TS types are not listed. I suspect &lt;code&gt;enum&lt;/code&gt;s.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All in all, it's safe to think of &lt;code&gt;unknown&lt;/code&gt; as "the set of all possible JS values".&lt;/p&gt;

&lt;p&gt;&lt;code&gt;any&lt;/code&gt; is the weird one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;any extends string ? 1 : 0&lt;/code&gt; evaluates to &lt;code&gt;0 | 1&lt;/code&gt; which is basically a "dunno".&lt;/li&gt;
&lt;li&gt;Even &lt;code&gt;any extends never ? 1 : 0&lt;/code&gt; evaluates to &lt;code&gt;0 | 1&lt;/code&gt;, meaning that &lt;code&gt;any&lt;/code&gt; &lt;em&gt;might&lt;/em&gt; be empty.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We should conclude that &lt;code&gt;any&lt;/code&gt; is "some set, but we're not sure which one" — like a type &lt;code&gt;NaN&lt;/code&gt;. However, upon further inspection, &lt;code&gt;string extends any&lt;/code&gt;, &lt;code&gt;unknown extends any&lt;/code&gt; and even &lt;code&gt;any extends any&lt;/code&gt; are all true, none of which holds for "some set". So, &lt;code&gt;any&lt;/code&gt; is a &lt;em&gt;paradox&lt;/em&gt; — every set is a subset of &lt;code&gt;any&lt;/code&gt;, but &lt;code&gt;any&lt;/code&gt; &lt;em&gt;might&lt;/em&gt; be empty. The only good news I have is that &lt;code&gt;any extends unknown&lt;/code&gt;, so &lt;code&gt;unknown&lt;/code&gt; is still the universe, and &lt;code&gt;any&lt;/code&gt; does not allow "alien" values.&lt;/p&gt;

&lt;p&gt;So, to finish mapping our types, we wrap our entire diagram into &lt;code&gt;unknown&lt;/code&gt; bubble:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fall.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thoughtspile.tech%2Fimages%2Fts-sets%2Fall.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Today, we've learnt to that TS &lt;em&gt;types&lt;/em&gt; are basically &lt;em&gt;sets&lt;/em&gt; of JS values. Here's a little dictionary to go from type-world to set-world, and back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our universe = all JS values = the type &lt;code&gt;unknown&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;never&lt;/code&gt; is an empty set.&lt;/li&gt;
&lt;li&gt;Subtype = narrowed type = subset, supertype = widened type = superset.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;A extends B&lt;/code&gt; can be read as "A is subset of B".&lt;/li&gt;
&lt;li&gt;Union and intersection types are, really, just set union and intersection.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Exclude&lt;/code&gt; is an &lt;em&gt;approximation&lt;/em&gt; of set difference that only works on union types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Going back my our initial questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0 | 1 extends 0&lt;/code&gt; is false because &lt;em&gt;{0,1}&lt;/em&gt; is &lt;em&gt;not&lt;/em&gt; a subset of &lt;em&gt;{0}&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;|&lt;/code&gt; work on sets, not on object shapes. &lt;code&gt;A &amp;amp; B&lt;/code&gt; is a set of things that satisfy both &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unknown&lt;/code&gt; is the set of all JS values. &lt;code&gt;any&lt;/code&gt; is a paradoxical set that includes everything, but &lt;em&gt;might&lt;/em&gt; also be empty.&lt;/li&gt;
&lt;li&gt;Intersecting with &lt;code&gt;never&lt;/code&gt; gives you &lt;code&gt;never&lt;/code&gt; because it's an empty set. &lt;code&gt;never&lt;/code&gt; has no effect in a union.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const x: {} = true;&lt;/code&gt; works because TS interfaces work by &lt;em&gt;constraining&lt;/em&gt; the property values, and we haven't constrained anything here, so &lt;code&gt;true&lt;/code&gt; fits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We still have a lot of TS mysteries to solve, so stay tuned!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Seven habits of bad interviewers</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Mon, 28 Mar 2022 12:11:56 +0000</pubDate>
      <link>https://forem.com/thoughtspile/seven-habits-of-bad-interviewers-1p7k</link>
      <guid>https://forem.com/thoughtspile/seven-habits-of-bad-interviewers-1p7k</guid>
      <description>&lt;p&gt;I’ve been to plenty of bad interviews. Sometimes, only some &lt;a href="https://blog.thoughtspile.tech/2022/03/21/bad-tech-interview/"&gt;questions are bad,&lt;/a&gt; but usually it goes further than that. Bizarre questions like “what’s the difference between a number and an array” are just a symptom of deeper issues.&lt;/p&gt;

&lt;p&gt;Let’s take a step back — &lt;em&gt;why&lt;/em&gt; are we interviewing? To hire someone — in our case, a software developer — good at building working software (or at least, ahem, with a good outlook), in &lt;em&gt;our&lt;/em&gt; team. A good interview process is precise (not hire people who can’t build software, and not reject people who can), and fast (to save our team’s time, and hire the candidate before anyone else does). That’s it, really. Hire good candidates with a reasonable precision / time ratio.&lt;/p&gt;

&lt;p&gt;If this sounds trivial, then why do some interviewers…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enjoy making the candidate look stupid?&lt;/li&gt;
&lt;li&gt;Ask questions that every sane person googles in real-life?&lt;/li&gt;
&lt;li&gt;Not help a struggling candidate as they would a struggling teammate?&lt;/li&gt;
&lt;li&gt;Ask random questions off the top of their head instead of planning ahead?&lt;/li&gt;
&lt;li&gt;Or, conversely, stick to a hard plan even as it stops making any sense?&lt;/li&gt;
&lt;li&gt;Spend time asking things that are on your CV?&lt;/li&gt;
&lt;li&gt;Have four interviews where one would suffice?&lt;/li&gt;
&lt;li&gt;Get into large groups, interrupting each other?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because they’re bad at interviewing, that’s why. Or, being more positive — because they’ve lost sight of the &lt;em&gt;goal&lt;/em&gt; to be achieved behind the &lt;em&gt;ritual.&lt;/em&gt; In this article, I go over the major sources of my frustration with interviews, and share some advice for improving your interviews.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TNy4Gicr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TNy4Gicr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits.png" alt="" width="880" height="671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Me smart — you stupid
&lt;/h2&gt;

&lt;p&gt;You can’t do much worse when interviewing than adopt “me smart — you stupid” mentality. Let’s face it, feeling smart is nice. The interviewer has all the answers, while the candidate doesn’t. It’s tempting to act like a genius from another planet who knows everything and be like “oh come on think again” and “even my grandmother can do it”. Bad.&lt;/p&gt;

&lt;p&gt;This approach obviously misses the whole point of interviewing — we’re trying to &lt;em&gt;hire&lt;/em&gt; someone, not &lt;em&gt;humiliate&lt;/em&gt; as many people as possible. As such, it’s more evident in larger companies, where the interviewer is often &lt;em&gt;not&lt;/em&gt; the person looking to strengthen his team, but some random fellow whose primary objective is to have lunch ASAP.&lt;/p&gt;

&lt;p&gt;I also call these interviews exam-like. To &lt;em&gt;pass,&lt;/em&gt; you need to give a &lt;em&gt;correct answer&lt;/em&gt; to each question on a list. Not a good model for an exam either, but it’s what many of us grew up with. Anyways, an &lt;em&gt;interview&lt;/em&gt; is certainly not an exam. The &lt;em&gt;examiner&lt;/em&gt; is much more knowledgeable than the &lt;em&gt;student,&lt;/em&gt; which is often not true for interviews (especially not middle / senior-level ones). Most exams cover specific known topics, while strict developer curricula don’t exist. Finally, while both exams and interviews can be a selection mechanism, exams have an extra goal of giving the &lt;em&gt;student&lt;/em&gt; an objective overview of his abilities. You see, different things.&lt;/p&gt;

&lt;p&gt;So, &lt;em&gt;never&lt;/em&gt; assume you’re smarter or somehow better than the candidate. But this is just the first step away from exam-like interviews — two other, less evident treats are focusing on the answer and not giving any hints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus on the answer
&lt;/h2&gt;

&lt;p&gt;A common feature of exam-like interviews is the checklist approach. The interviewer asks the question, the candidate answers, we up the score if the answer fits, and move on. The questions therefore tend to be very closed, to facilitate checking &lt;em&gt;correctness,&lt;/em&gt; and the difficulty level is tuned by choosing more esoteric topics: a junior JS developer tells about &lt;em&gt;let vs const,&lt;/em&gt; the senior — about &lt;em&gt;the event loop.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Real software development is rarely about quickly answering very specific questions. In fact, “how to check browser support for server-sent events” is the most minute detail usually fixed by googling at the final stage of problem-solving. What is it about, then? Many things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Brainstorming possible approaches&lt;/li&gt;
&lt;li&gt;Decomposing a task into bite-sized subtasks&lt;/li&gt;
&lt;li&gt;Iterating on the idea to find weak points&lt;/li&gt;
&lt;li&gt;Collaborating with teammates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And I haven’t even touched the actual coding yet. Not saying code interviews are worthless, but you get more bang for your buck and test several skills at once by focusing on the problem-solving rather than the &lt;em&gt;right answer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Problem-solving naturally favors more open-ended questions like “design a slider gallery”, over “what touch events exist”, because they have more process, and thus avoid most of the pitfalls I described in my earlier &lt;a href="https://dev.to/2022/03/21/bad-tech-interview/"&gt;article on bad interview questions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; answer-centric questions work well for screenings. A non-technical recruiter can ask a few sane questions like “What are some React hooks?” and I’m like “useRef useMemo useEffect”, and we know I’m legit. The lack of trust is &lt;em&gt;slightly&lt;/em&gt; annoying, but I’ve seen many candidates who can’t tell an iterator from a cucumber, so I feel you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not giving any hints
&lt;/h2&gt;

&lt;p&gt;The final treat of “exam-like” interviews that can persist in &lt;em&gt;realistic&lt;/em&gt; coding tasks is leaving the candidate alone with the problem, not helping where it’s due. The thinking is that a &lt;em&gt;senior-level developer&lt;/em&gt; must know this, and if I don’t — I’m obviously not one. Again, that’s not how development works.&lt;/p&gt;

&lt;p&gt;Do you immediately fire or report your teammate who misses a corner case, has room for improvement, or is completely lost? Hopefully not. Collaboration is a key development skill, and you help your friend out, right? Why, then, should the interview be the other way around? Sure,it’s best to plan for edge cases in advance, still cool to identify them yourself and iterate, but it’s not too bad if you can admit and fix a mistake pinpointed to you.&lt;/p&gt;

&lt;p&gt;Worst case — don’t even show you’re not satisfied with the answer at all. “So, this is your final answer? Let’s move on then” (to yourself: “Stupid stupid Vladimir, I’ll never hire you”). Happened to me — I solved a &lt;a href="https://www.geeksforgeeks.org/detect-loop-in-a-linked-list/"&gt;particular algorithm problem&lt;/a&gt; in linear memory three times in three separate interviews until one interviewer told me that an constant-memory solution actually exists. Until then, I believed the first two rejected me just because they disliked my attitude.&lt;/p&gt;

&lt;p&gt;There’s no shame in helping a struggling candidate!&lt;/p&gt;

&lt;h2&gt;
  
  
  Poor planning
&lt;/h2&gt;

&lt;p&gt;Many interviewers drop in from their daily job with a “fuck, I’m interviewing in 3 minutes!”, don’t have a question list ready because “I’m a senior engineer, I’ll know my senior buddy from a mile”, and proceed with some trivia from a random “best question list” found online (during the interview, of course) or the last tricky thing they’ve done in their job — both poor choices. I know this because I’ve done it myself, and I’m sorry. Planning is king.&lt;/p&gt;

&lt;p&gt;Let’s handle the questions first. Ideally, you want to cover several topics related to the role. In a front-end interview these are probably JS/TS, CSS, and some React (or whatever you use) / algo / deployment / performance. Nice way to go through a topic: a few closed question -&amp;gt; open question -&amp;gt; full-on &lt;em&gt;software design&lt;/em&gt; task. There are many interdependencies and constraints, so trying to come up with questions on the fly is guaranteed to fail unless you interview daily.&lt;/p&gt;

&lt;p&gt;Timing is not as important as you think. Finishing early is fine — whether the candidate aces all the questions in 10 minutes, or fails even the most basic ones, once you got what you came for, filler talk is not the best use for leftover time. Running badly over-time is worse, because something might be scheduled right after the interview, but easily fixed by announcing the duration with a 30-minute extra, e.g. plan for 1.5 hours for a 1-hour interview.&lt;/p&gt;

&lt;p&gt;While we’re at it, locking yourself into a very strict script is no better than having no plan at all. If the question sparks some interesting discussion, don’t kill it just to ask more low-level stuff. If you were looking for a senior developer, but happen to have a good junior before you, you’d better adapt — a good junior is still useful and hard to come by. If the candidate has never worked on performance optimization, there’s no use asking in-depth about TTI measurement. Open-ended tasks give you more flexibility in all these cases than “senior-only” questions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xX_AQXpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits-plan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xX_AQXpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits-plan.png" alt="" width="856" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, do prepare the list of questions that reasonably cover the topics you care about, but don’t obsess over that plan too much — at the end of the day, you’re after a candidate to reinforce your team, not a walking encyclopedia of development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignoring the CV
&lt;/h2&gt;

&lt;p&gt;Interviews are precious face time with the candidate best used to get to know each other, judge social and problem-solving skills. Why waste this opportunity on reiterating things that are evident from the CV?&lt;/p&gt;

&lt;p&gt;I worked on large-scale products in big tech companies, got a degree from a top university, and even have a blog and some open-source work — it’s all on my CV, and learning it takes 15 mins before the interview. Not bragging, but it shows I’m probably not too bad. Now, you have a right to be suspicious — maybe I’m a con artist who just made up my CV and forked some repos. A few low-level questions like “sum numbers in an array” are fine. But why spend an hour on “now, write a FUNCTION” and “have you ever worked in a team”? You haven’t taken a single look at the data you had on me, have you, lazy bastard?&lt;/p&gt;

&lt;h2&gt;
  
  
  Overstretching
&lt;/h2&gt;

&lt;p&gt;Big tech companies are famous for lengthy interview processes: Google has about 8 interviews, and other tech giants are not far behind. So, as a 3-person startup that wants to be the next Google, you need to have many interviews, right? Not so fast. Large companies have many factors that let them get away with (and even require) lengthy interview processes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The candidates are motivated to work in this particular company and are less likely to accept faster offers while interviewing with you.&lt;/li&gt;
&lt;li&gt;A steady stream of candidates justifies high rejection rates &lt;em&gt;and&lt;/em&gt; requires ranking precision only achieved by collecting more data.&lt;/li&gt;
&lt;li&gt;Many employees available for interviewing. It’s fine to spend 80 hours on 10 candidates when it’s 0.02% of your team’s time, not so much when it’s 67%.&lt;/li&gt;
&lt;li&gt;Many teams are hiring at any time, and they reasonably want to have some personal time with the candidates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’m fine with 2 interviews and a phone screening, as long as I genuinely like your place, and you can schedule them within a week or two. I’m a lot less enthusiastic about spending 4 hours with a very talkative team lead in your average outsource agency when I can get a comparable offer in a day. Requiring a larger time commitment shows how amazing &lt;em&gt;you&lt;/em&gt; think you are. You’d better really excite the candidate more than the companies with shorter hiring processes do.&lt;/p&gt;

&lt;p&gt;Having &lt;em&gt;too many&lt;/em&gt; interviews is not the only way to overstretch the process. You can only have two, scheduled too far from each other, or be generally slow to organize them and gather feedback. I’m no expert on office administration, but remember that your goal is to hire people &lt;em&gt;before&lt;/em&gt; they accept another offer, so please do your best here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hXZueMPI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits-overstretching.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hXZueMPI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits-overstretching.png" alt="" width="880" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, &lt;em&gt;only&lt;/em&gt; add more interviews if you really need them, and your offering justifies the extra time required. Also remember that extra interviews are not free for your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Poor group dynamics
&lt;/h2&gt;

&lt;p&gt;Luckily, 1-on-1 interviews are the standard. However, there are good reasons to have more interviewers: you get a second opinion (as we say in Russia, “one head is good, but two are better”), and several teams that are hiring get to see the candidate in action. However, I often see problems with group dynamics.&lt;/p&gt;

&lt;p&gt;Several interviewers, competing and interrupting each other, is just a mess. This might work if you’re making a &lt;em&gt;normal&lt;/em&gt; (as possible under the circumstances) conversation without the whole question-answer thing, but these &lt;em&gt;informal&lt;/em&gt; interviews have their drawbacks — prioritizing social skills and making it hard to compare many candidates. In a traditional QnA format, it’s better to have &lt;em&gt;one&lt;/em&gt; interviewer in charge at any time, with others &lt;em&gt;observing.&lt;/em&gt; You can switch roles by section or by answer if you like. But still, there are dangerous spots.&lt;/p&gt;

&lt;p&gt;One pitfall is follow-up questions. Every interviewer wants to ask one, then it gets out of control and the follow-ups add no value beyond showing the second interviewer’s here. Once I’ve interviewed with 8 (yes, eight) people at once, and once they were done with their follow-ups to the first question, the time was up and nobody seemed really happy. You often get some back-end dude hiding in the dark to assault you with “OK you can paint your buttons, but can you make me a fault-tolerant DB persistence layer” — wut? Make sure every follow-up helps you assess the candidate better, and isn’t just something that sprung to your mind (see section on not planning ahead).&lt;/p&gt;

&lt;p&gt;Another problem that arises with a “passive” interviewer is boredom. Listening to the same answers for the tenth time is not always exciting. However, it’s not very reassuring to see a visibly bored interviewer, especially one who starts playing with the phone because business. The worst of I’ve seen a bored interviewer do is reach into his pants, take something out and chew on it. Hope it was a candy. I needed time to accept this experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b-XDL2Yp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits-group.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b-XDL2Yp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/bad-interview-habits-group.png" alt="" width="880" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you bring buddies to the interview, make sure to agree on who asks what in advance, avoid useless follow-ups that assert your smartness, and make sure not to fall asleep when it’s not your talking time.&lt;/p&gt;




&lt;p&gt;Many things can make interviews a horrible experience. Here are some tips to be a better interviewer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Focus on finding out what the candidate is good at, not on showing off how smart you are. Yes, even if you’re not hiring for yourself.&lt;/li&gt;
&lt;li&gt;Seeing how the candidate approaches problems, is better than getting some particular answer you expect. Open questions suit this style better.&lt;/li&gt;
&lt;li&gt;If the candidate struggles, help out! People &lt;em&gt;will&lt;/em&gt; get stuck in real life, and acting on feedback is a useful skill.&lt;/li&gt;
&lt;li&gt;Prepare a question plan that reasonably covers the topics you care about — it’s hard to come up with questions as you go.&lt;/li&gt;
&lt;li&gt;But also make sure the plan is flexible enough to allow for unexpected turns. Is hiring a senior engineer experienced in performance optimization &lt;em&gt;really&lt;/em&gt; the only good outcome? Don’t you think people are capable of, uhm, learning?&lt;/li&gt;
&lt;li&gt;Get as much data on the candidate as possible beforehand — CV, open source work, shared acquaintances and previous interview results are your friends. Asking this again and again is a waste of precious face time.&lt;/li&gt;
&lt;li&gt;Don’t make the interview process longer than it needs to be with more interviews or poor scheduling. If you add an interview, you’d better be sure the candidate will want to invest the time in it.&lt;/li&gt;
&lt;li&gt;If you have several interviewers, agree on who asks what, make sure the follow-up questions add value beyond showing you’re here, and try not to fall asleep when not talking. Alternatively, try a “conversation-based” interview if it works for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Above all, keep you end goal in mind — to hire someone who’ll help you build your product. Happy interviewing!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>interview</category>
      <category>career</category>
    </item>
    <item>
      <title>5 coding interview questions I hate</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Mon, 21 Mar 2022 16:26:29 +0000</pubDate>
      <link>https://forem.com/thoughtspile/5-coding-interview-questions-i-hate-fpa</link>
      <guid>https://forem.com/thoughtspile/5-coding-interview-questions-i-hate-fpa</guid>
      <description>&lt;p&gt;I’ve taken part in well over a hundred tech interviews now, on both sides. Some were fun, and some were pure cringe. I’ve been asked if I have kids (supposedly, people with children won’t have time to job hop), and if “I bet my ass I cost that much”. Fun times.&lt;/p&gt;

&lt;p&gt;But today I’d like to talk about cringe tech interview question posing as valid ones. They’re on topic, but they do nothing but annoy and frustrate the candidate. Some are beyond saving, while some are fine, but often mishandled by the interviewer. Here are the main offenders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens if you build a circular prototype chain? And other intervew trivia.&lt;/li&gt;
&lt;li&gt;How to migrate from webpack 3 to webpack 5? And other specifics.&lt;/li&gt;
&lt;li&gt;What’s the difference between a number and an array? And other questions obfuscated with fuzzy wording.&lt;/li&gt;
&lt;li&gt;What’s the fastest way to convert a string to number? And other unspecified behavior.&lt;/li&gt;
&lt;li&gt;How to make this code sample &lt;em&gt;better&lt;/em&gt;? And other questions with missing context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll share tips both for the interviewers to spend their time better, and for the candidates who hit some of these. Let’s go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Trivia
&lt;/h2&gt;

&lt;p&gt;This first category comprises questions that are popular in interviews, but rarely arise in practice. What happens when we run the following code?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;__proto__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;__proto__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We modify the prototype chain in runtime to make it circular. You’ve probably guessed the answer is “nothing good”. You’d never write code like this in production. What a maniac would use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto"&gt;the ad-hoc standardized and since deprecated &lt;code&gt;__proto__&lt;/code&gt; property?&lt;/a&gt;) Of course, the “correct” answer is that &lt;code&gt;y. __proto__ = x&lt;/code&gt; throws &lt;code&gt;TypeError: Cyclic __proto__ value&lt;/code&gt;, thanks, that’s good to know.&lt;/p&gt;

&lt;p&gt;These questions only show how many interviews the candidate has been in, and it’s in case you’ve picked a popular question. If you’ve just come up with a “tricky question that’s not on the web” yourself, it’s just a dice roll of spec memorization.&lt;/p&gt;

&lt;p&gt;Old-style JS questions are a notable sub-category of trivia. Yes, those of us who started programming back before 2015 know how &lt;code&gt;var&lt;/code&gt; hoisting works, and how to declare (and even extend) a class with &lt;code&gt;.prototype&lt;/code&gt; Here, you’re effectively testing when the developer started learning JS (or if he’s worked on a legacy codebase), which is already on the CV. &lt;em&gt;Maybe&lt;/em&gt; worth it if you have a legacy project yourself.&lt;/p&gt;

&lt;p&gt;As an interviewer, avoid questions that have nothing to do with the real-life development. The biggest fans of these questions are non-technical people (CEOs, founders, PMs) trying to conduct coding interviews by going over a question list found online. Bad news — it doesn’t work like that. Either hire / borrow a real developer just for the interviews, or stick to soft-skill topics and experience anecdotes.&lt;/p&gt;

&lt;p&gt;The only way to answer these is, sadly, to participate in many interviews — or, as a cheap substitute, to browse lists of “TOP JS questions ever, guaranteed”. No one can realistically know every corner case of the language and the libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specifics
&lt;/h2&gt;

&lt;p&gt;What issues does migrating to webpack 6 have? How do you check browser support for server-sent events? What &lt;code&gt;aria-role&lt;/code&gt; should a modal page have? I don’t know. I usually google for this kind of things, and I’m pretty good at it.&lt;/p&gt;

&lt;p&gt;This is what happens when you take my advice on using &lt;em&gt;real-life programming&lt;/em&gt; questions a bit too seriously, and drop random problems you recently solved into an interview. Obviously, you’ve made your research, figured out the answer, and are pleased with yourself. But I’m not you, we have different experience to complement each other — and isn’t that even better? Don’t label me as an &lt;em&gt;idiot&lt;/em&gt; just because I’ve never touched a certain area of web development.&lt;/p&gt;

&lt;p&gt;Instead, ask the candidate about a challenging problem &lt;em&gt;she&lt;/em&gt; recently solved — it’s going to be far more entertaining and valuable for both of you.&lt;/p&gt;

&lt;p&gt;If you’re asked this question, and have never faced the problem yourself, you’re in trouble. Try to access all the background knowledge around the topic you might have, and &lt;em&gt;maybe&lt;/em&gt; you can even figure this out based on common sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obfuscated questions
&lt;/h2&gt;

&lt;p&gt;“What’s the difference between a number and an object” is the most surprising question ever — double the surprise every time I hear it yet again. Come on, what’s the difference between your head and a sweet potato fresh out of oven? Excuse me, am I interviewing for a comedian?&lt;/p&gt;

&lt;p&gt;The problem here is that a very concrete answer is expected (I figured out it’s “number is immutable”), but asking it directly is “too simple”, so you dress it up by presenting it as open-ended, and add some fuzziness, but in the end it just becomes impossible to decipher what you were trying to ask in the first place.&lt;/p&gt;

&lt;p&gt;The worst part is that you can’t even give a hint, because the hint is the answer, so you’re bound to circle around confusing everyone further. “What can you do with numbers that you can’t with objects?” (Add them? Coerce to &lt;code&gt;false&lt;/code&gt;?). “Think again, do numbers have properties?” (in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number"&gt;a way,&lt;/a&gt; they do). What a waste of time!&lt;/p&gt;

&lt;p&gt;Again, interview experience is key here. Once you wrap your head around the &lt;a href="https://dev.to/macmacky/70-javascript-interview-questions-5gfi"&gt;common JS interview questions&lt;/a&gt; and topics, you’ll get better at recognizing them whatever costume they’re dressed in.&lt;/p&gt;

&lt;p&gt;As an interviewer, prefer direct questions (“Which JS types are immutable?”) or at least make sure your wording points in the intended direction, not all over the place (“What’s the difference between primitive types and objects?”).&lt;/p&gt;

&lt;h2&gt;
  
  
  Unspecified behavior
&lt;/h2&gt;

&lt;p&gt;Will &lt;code&gt;console.log(Object.keys({ x: 0, y: 0 }).join())&lt;/code&gt; log &lt;code&gt;x,y&lt;/code&gt; or &lt;code&gt;y,x&lt;/code&gt;? Is &lt;code&gt;x[key]&lt;/code&gt; faster than &lt;code&gt;x.find(e =&amp;gt; e.key === key)&lt;/code&gt;? Under normal circumstances I know the answers are &lt;code&gt;x,y&lt;/code&gt; (addition order) and &lt;em&gt;yes,&lt;/em&gt; but it’s not on the spec, so I wouldn’t over-rely on it.&lt;/p&gt;

&lt;p&gt;Performance-related questions are the most popular of this bunch. JS runtimes change fast, and even if you’ve seen some code perform better a few years ago, it might not be the case any more. The fact that built-in data structures in JS do not specify operation complexity does not help either. We reasonably &lt;em&gt;assume&lt;/em&gt; object access &lt;code&gt;obj[key]&lt;/code&gt; to take &lt;em&gt;O(1),&lt;/em&gt; but in reality this depends on many factors — object size, insert / remove frequency, whether the keys are string literals (see &lt;a href="https://esbench.com/bench/62385aeb6c89f600a57016a7"&gt;benchmark&lt;/a&gt;). And even when the difference does exist, it’s usually abysmal and irrelevant to actual front-end development.&lt;/p&gt;

&lt;p&gt;Overall, these questions are good for assessing both the practical knowledge of the common-case performance, the difference between runtimes, &lt;em&gt;and&lt;/em&gt; how it applies to everyday development. As an interviewer, you only need two things to handle this well: be aware of the implementation-dependence yourself and have an open mind when it comes to unexpected answers. These questions are certainly &lt;em&gt;not&lt;/em&gt; suitable for a non-interactive interview, such as a single-choice online survey (Pick the fastest method: a) &lt;code&gt;a.pop()&lt;/code&gt; b) &lt;code&gt;a.shift()&lt;/code&gt; c) &lt;code&gt;a.length -= 1&lt;/code&gt;), because there’s no one right answer here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Missing-context questions
&lt;/h2&gt;

&lt;p&gt;Open-ended questions are among the best you can ask in an interview — they’re challenging, and reveal both the candidate’s problem-solving skills and real-world experience. However, these questions are unforgiving to lazy interviewers — both sides should put in some effort to make them work. The two prime ways to mess these up are being uncollaborative and locking in to one possible solution.&lt;/p&gt;

&lt;p&gt;For example, what’s wrong with this function?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why, looks perfectly fine to me. Or maybe not. Depends on what you’re trying to achieve. The code is rarely “right” or “wrong” by itself. Yes, mutating &lt;code&gt;arr&lt;/code&gt; is non-idiomatic lately, but maybe it works in-place as an optimization. Yes, the use of &lt;code&gt;var&lt;/code&gt; is frowned upon, but maybe it’s a library that’s supposed to work in IE6. Yes, we could replace the &lt;code&gt;for (;;)&lt;/code&gt; with a &lt;code&gt;.map&lt;/code&gt; or a &lt;code&gt;for..of&lt;/code&gt;, but then we’d lose support for all array-likes. See, it all depends on the context. I can endlessly produce variations of this code, but I’d rather not if it’s not causing any trouble.&lt;/p&gt;

&lt;p&gt;This question is often presented as “refactor this code to make it BETTER” — sure to excite any junior developer. Good thing grumpy old Vladimir is here to tell you that “refactoring” with no clear goal is the greatest waste of time out there.&lt;/p&gt;

&lt;p&gt;To illustrate the solution lock-in problem, suppose you give me a slider gallery implemented with a horrendous mess of jQuery and ask to &lt;em&gt;improve&lt;/em&gt; it. Problem is, you believe the best way to spend an hour on this is to write unit tests and convert to React. I think I should change jQuery to vanilla DOM and improve UX with better overscroll handling. Neither of us is right or wrong. In real life, the project goals and technical direction sets the context — how many jQuery dependencies we have across the codebase, if we intend on migrating to React, manual QA availability.&lt;/p&gt;

&lt;p&gt;When asking an open-ended question, either let go of your expected answer and focus on the problem-solving process, or introduce the missing requirements to guide towards your desired solution.&lt;/p&gt;




&lt;p&gt;So, here are the five types of interview questions that I hate, most hopeless to nice-but-dangerous:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trivia: questions that often arise in interview, but never in real life, e.g. “what happens if you create a circular prototype chain”. These test for interview experience, not development skills. Avoid.&lt;/li&gt;
&lt;li&gt;Specifics: solutions to unique real-life problems. Example: “how to migrate webpack 3 to webpack 5”. &lt;em&gt;Might&lt;/em&gt; work if the answer can be from the expected background knowledge or if the area is super critical to your product. Instead, ask about a challenging problem the candidate recently solved.&lt;/li&gt;
&lt;li&gt;Obfuscated questions: simple questions made &lt;em&gt;harder&lt;/em&gt; by fuzzy wording, such as “what’s the difference between a number and an object?” instead of “are JS numbers mutable?”. Avoid.&lt;/li&gt;
&lt;li&gt;Implementation questions: the answer depends on a specific JS runtime, e.g. “what’s the fastest way to loop over a list”. Avoid strong wordings and don’t bikeshed if the answer is not what you expected.&lt;/li&gt;
&lt;li&gt;Missing context: answer requires additional details, e.g. “how to improve this code”. Amazing question as long as you’re ready to problem-solve together, provide the missing constraints and give credit for noting the distinct possibilities. Worst question if you expect a specific question while many objectively good alternatives exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s my advice on improving interview questions, as a nice flowchart:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_aBp4ghL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/interview-guide.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_aBp4ghL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/interview-guide.png" alt="" width="880" height="860"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For candidates, I’m afraid, I have fewer actionable tips. Sure, practice makes perfect, and skimming the common interview questions won’t hurt even if you’re a hot shot lead senior, but other than that it’s hard to escape a bad question with an bad interviewer. You can’t convince the interviewer to change the question or his approach on the fly (if you can, apply for a top-tier sales representative position instead). Tough luck, but be ready to admit you don’t know some things, and try to work from the knowledge you have around the topic.&lt;/p&gt;

&lt;p&gt;Good luck with your interviews, and see you later!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>interview</category>
      <category>career</category>
    </item>
    <item>
      <title>Using global memoization in React</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Wed, 16 Feb 2022 08:28:06 +0000</pubDate>
      <link>https://forem.com/thoughtspile/using-global-memoization-in-react-1267</link>
      <guid>https://forem.com/thoughtspile/using-global-memoization-in-react-1267</guid>
      <description>&lt;p&gt;When our React apps get slow, we usually turn to &lt;code&gt;useMemo&lt;/code&gt; to avoid useless job on re-render. It’s a &lt;em&gt;hammer&lt;/em&gt; that often works well, and makes it hard to shoot yourself in the foot. But &lt;code&gt;useMemo&lt;/code&gt; is not a silver bullet — sometimes it just introduces more useless work instead of making your app faster.&lt;/p&gt;

&lt;p&gt;In this article, I explore the less conventional caching techniques in React that can do wonders to optimize your apps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, we must understand exactly how &lt;code&gt;useMemo&lt;/code&gt; works — and why.&lt;/li&gt;
&lt;li&gt;What are some use cases where &lt;code&gt;useMemo&lt;/code&gt; does not help much?&lt;/li&gt;
&lt;li&gt;Then, we examine four global caching methods, where cache is shared between components. As usual, they come with different tradeoffs, and some are even dangerous if used carelessly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There’s a neat cheat sheet awaiting you at the end. Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Inside useMemo
&lt;/h2&gt;

&lt;p&gt;To see if &lt;code&gt;useMemo&lt;/code&gt; fits our particular use case, we must know how, precisely, it works. To quote &lt;a href="https://reactjs.org/docs/hooks-reference.html#usememo" rel="noopener noreferrer"&gt;the docs,&lt;/a&gt; &lt;em&gt;useMemo will only recompute the memoized value when one of the dependencies has changed.&lt;/em&gt; This is rather ambiguous, so let’s check against &lt;a href="https://github.com/facebook/react/blob/9d4e8e84f7fb782385d81ffcdcda73822acf4ad1/packages/react-reconciler/src/ReactFiberHooks.new.js#L1906" rel="noopener noreferrer"&gt;the implementation:&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The cache is initialized when mounting a component instance, and destroyed when unmounting.&lt;/li&gt;
&lt;li&gt;The cache is never shared between different component instances.&lt;/li&gt;
&lt;li&gt;The cache stores just a single value — the last one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a sensible default. Storing &lt;em&gt;one&lt;/em&gt; value never leaks memory, even if you use an unstable dependency. Say our memo (and &lt;code&gt;useCallback&lt;/code&gt; is just a wrapper over &lt;code&gt;useMemo&lt;/code&gt;) depends on an unstable arrow, &lt;code&gt;onClick&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we create a new &lt;code&gt;handleClick&lt;/code&gt; on every render. If &lt;code&gt;useMemo&lt;/code&gt; stored all the previous values, every &lt;code&gt;handleClick&lt;/code&gt; would occupy memory forever — bad. Also, storing N values requires N dependency comparisons when reading, which is N times slower than checking once. Sure, &lt;code&gt;useMemo&lt;/code&gt; is worthless here, but at least it does not explode.&lt;/p&gt;

&lt;p&gt;Localizing cache to a single component guards against missing deps. Suppose you’re sure a scope variable &lt;em&gt;never&lt;/em&gt; changes during the component lifetime, so you just omit it from the dependency array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;clicks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setClicks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nf"&gt;setClicks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If&lt;/em&gt; the cache was shared among multiple components, distinct &lt;code&gt;handleClick&lt;/code&gt;s would call the same &lt;code&gt;setClicks&lt;/code&gt;, so only one counter would increment — unexpected!&lt;/p&gt;

&lt;p&gt;Good job, React team — thanks for saving us the trouble of debugging this! But this safe implementation has its limitations.&lt;/p&gt;

&lt;h2&gt;
  
  
  useMemo pitfalls
&lt;/h2&gt;

&lt;p&gt;While a great default, the locality and single-value limit of &lt;code&gt;useMemo&lt;/code&gt; make it useless in some scenarios. For example, consider this attempt at memoizing a large city list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RouteItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; 
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Moscow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MOW&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saint Petersburg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 1000 more cities], []); &lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we render a 1000 &lt;code&gt;RouteItem&lt;/code&gt;s, each one gets its own array, which is wasteful. In this case, we’d prefer sharing the cache between different instances.&lt;/p&gt;

&lt;p&gt;Another problem point is alternating dependency values. Let’s say we want to generate color scheme based on checkbox value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SchemePicker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; 
    &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isDark&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isDark&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setDark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
      toggle theme 
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we only have two possible dependency values, &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;, so there is no risk of a memory leak. Yet, on every checkbox change, we compute a fresh color scheme. The old one would be just fine, thank you.&lt;/p&gt;

&lt;p&gt;So, in some cases we’d like to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Share cache between different component instances.&lt;/li&gt;
&lt;li&gt;Remember several values, not just the last one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No problem, with the power of JS at our disposal we can make it happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Global memo
&lt;/h2&gt;

&lt;p&gt;If we want to reuse a value between component instances, no hook can save us, because both &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useRef&lt;/code&gt; are local to component instance. But we can extract the cache into module scope, and work from there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// this is shared between all components&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/* some cache */&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="c1"&gt;// cache is always the same object &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Precomputed global constant
&lt;/h3&gt;

&lt;p&gt;The simplest kind of “cache” is one with no dependencies — a constant that’s usable in every component. And the simplest solution is to just to declare this constant right away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Moscow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MOW&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saint Petersburg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="c1"&gt;// 1000 more cities&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="c1"&gt;// yay, every RouteItem refers to the same cities&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RouteItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having just &lt;em&gt;one&lt;/em&gt; value for all components seems limiting. But, if we know all the possible dependency values in advance, we can just precompute the value for each dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schemes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="na"&gt;light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SchemePicker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="c1"&gt;// we only have 2 values, each one is stable &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schemes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setDark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
      toggle theme 
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this technique comes with some drawbacks. Building the object in the initial execution phase delays the first paint, even if you don’t need the value right away. All the data needed to construct the value must be available when the script is initially executed. If any of this is a concern, let’s move on to the next technique!&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy global constant
&lt;/h3&gt;

&lt;p&gt;So, we want to share a single value between all components, but we want to compute it only when we need it. Fine, it’s a well-known pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;citiesCache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// getCities intercepts accessing cities&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="c1"&gt;// use cached value if it exists &lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;citiesCache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;citiesCache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="c1"&gt;// otherwise put the array into the cache &lt;/span&gt;
  &lt;span class="nx"&gt;citiesCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Moscow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MOW&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saint Petersburg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="c1"&gt;// 1000 more cities&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;citiesCache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RouteItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;getCities&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we delay building the value until we actually need it. Great! And we could even pass some data from an API to the builder, as long as it never changes. Fun fact: storing data in a state manager or an API cache is actually an example of this technique.&lt;/p&gt;

&lt;p&gt;But what if we try to generalize this method for multiple values, just like we did with a precomputed map? Oh, that’s a whole different story!&lt;/p&gt;

&lt;h3&gt;
  
  
  True memo
&lt;/h3&gt;

&lt;p&gt;Let’s up our game by letting every component get a special version of city list, with one city excluded. We’d still like to share the cache between several instances, just in case. It’s not that hard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Moscow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MOW&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saint Petersburg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="c1"&gt;// 1000 more cities&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filterCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCitiesExcept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="c1"&gt;// use cached value if it exists &lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filterCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;filterCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="c1"&gt;// otherwise put the filtered array into the cache&lt;/span&gt;
  &lt;span class="nx"&gt;filterCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cities&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;filterCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RouteItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;getCitiesExcept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but global caches are vulnerable to infinite growth problem. In a long-lived app, you might eventually get to the point where every possible city was excluded, leaving you with a 1000 copies of your 1000-item array in the cache, most of them useless. To protect against this, we need some way to limit the cache size.&lt;/p&gt;

&lt;h3&gt;
  
  
  LRU cache
&lt;/h3&gt;

&lt;p&gt;To restrict cache size, we need some way to choose exactly which elements to “forget”. This is called &lt;a href="https://en.wikipedia.org/wiki/Cache_replacement_policies" rel="noopener noreferrer"&gt;&lt;em&gt;cache replacement policy,&lt;/em&gt;&lt;/a&gt; and there are surprisingly many approaches.&lt;/p&gt;

&lt;p&gt;We’ll stick to the simplest method — least-recently used, or LRU cache. We only remember N last values. For example, after passing numbers 1, 2, 3, 1 to an LRU cache of size 2, we only store the values for 3 and 1, while the value for 2 was thrown away. The implementation is not interesting, hope you believe this is doable (see &lt;a href="https://github.com/lukeed/flru/blob/master/src/index.js" rel="noopener noreferrer"&gt;flru&lt;/a&gt; for details). It’s worth noting that the original &lt;code&gt;useMemo&lt;/code&gt; is actually an LRU cache of size 1, because it only stores one last value.&lt;/p&gt;

&lt;p&gt;While it sounds good on paper, global bounded cache does not actually work that well for our use cases. To see why, let’s consider a cache of size 1. If we have several component instances alive at once, they &lt;em&gt;likely&lt;/em&gt; have different dependency values. If they render in alternating order, every instance encounters the value from the previously rendered one, which is a cache miss, and has to recompute. So, we end up recomputing on every render, and doing some useless comparisons.&lt;/p&gt;

&lt;p&gt;More generally, a cache of size N is likely to have misses once N+1 components with different values are alive, and become useless at 2N components. This is not a good quality — a cache shouldn’t care how many consumers exist. We could experiment with other replacement policies — say, frequency-based caches — but they’re way harder to implement, and I feel like React apps don’t have cache usage patterns that could benefit from them.&lt;/p&gt;

&lt;p&gt;There is, however, one case where it works: if you have N possible dependency values, and N is &lt;em&gt;small&lt;/em&gt; — say, &lt;code&gt;true&lt;/code&gt; / &lt;code&gt;false&lt;/code&gt;, or a number 1..10, a cache of size N has you fully covered with 100% cache hits, and only computes values when needed. But if that’s the case, a simple global cache works just the same, without the overhead of tracking usage order.&lt;/p&gt;




&lt;p&gt;Recap time! We’ve started out by looking at &lt;code&gt;useMemo&lt;/code&gt; in detail. &lt;code&gt;useMemo&lt;/code&gt; cache is never shared between component instances, lives as long as the instance lives, and only stores one last value. There are good reasons for these decisions.&lt;/p&gt;

&lt;p&gt;However, this makes &lt;code&gt;useMemo&lt;/code&gt; not usable in some cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When you &lt;em&gt;want&lt;/em&gt; to reuse a value between components (e.g. always the same large object)&lt;/li&gt;
&lt;li&gt;When your dependency quickly alternates between several values (e.g. true / false / true etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, we examined 4 (4-and-a-half? 5?) caching techniques with a globally shared cache that overcome these issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Just use a module constant. Simple, reliable, but builds the object during initial script execution — suboptimal if the object is heavy and not needed during initial render.&lt;/li&gt;
&lt;li&gt;Precomputed map — a simple extension of &lt;em&gt;module constant&lt;/em&gt; that stores several values. Same drawbacks.&lt;/li&gt;
&lt;li&gt;Lazy constant — delay building the object until it’s needed, then cache forever. Removes module constant init delay during script init time.&lt;/li&gt;
&lt;li&gt;Full memo — saves &lt;em&gt;all&lt;/em&gt; the results of function calls with &lt;em&gt;all&lt;/em&gt; arguments. Leaks memory when there are many possible dependency values / combinations. Good when there are few possible inputs. Use with care.&lt;/li&gt;
&lt;li&gt;Bounded cache (e.g. LRU). Fixes the memory leak problem, but useless when the number of components alive with different deps is larger than cache size. Not recommended.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a cheat sheet to help you remember these techniques:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthoughtspile.github.io%2Fimages%2Fglobal-memo-cheatsheet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthoughtspile.github.io%2Fimages%2Fglobal-memo-cheatsheet.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These techniques are useful in regular react apps, and can up your performance. But we don’t always need our cache to be shared between component instances. Luckily, all these methods also work when scoped to a component — stay tuned for the next post on alternate &lt;code&gt;useMemo&lt;/code&gt; implementations.&lt;/p&gt;

</description>
      <category>react</category>
      <category>hooks</category>
    </item>
    <item>
      <title>Good advice on JSX conditionals</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Mon, 17 Jan 2022 15:58:03 +0000</pubDate>
      <link>https://forem.com/thoughtspile/good-advice-on-jsx-conditionals-5baa</link>
      <guid>https://forem.com/thoughtspile/good-advice-on-jsx-conditionals-5baa</guid>
      <description>&lt;p&gt;Conditional rendering is a cornerstone of any templating language. React / JSX bravely chose not to have a dedicated conditional syntax, like &lt;code&gt;ng-if="condition"&lt;/code&gt;, &lt;a href="https://reactjs.org/docs/conditional-rendering.html"&gt;relying&lt;/a&gt; on JS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND"&gt;boolean operators&lt;/a&gt; instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;condition &amp;amp;&amp;amp; &amp;lt;JSX /&amp;gt;&lt;/code&gt; renders &lt;code&gt;&amp;lt;JSX /&amp;gt;&lt;/code&gt; iff &lt;code&gt;condition&lt;/code&gt; is truthy,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;condition ? &amp;lt;JsxTrue /&amp;gt; : &amp;lt;JsxFalse /&amp;gt;&lt;/code&gt; renders &lt;code&gt;&amp;lt;JsxTrue /&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;JsxFalse /&amp;gt;&lt;/code&gt; depending on the truthiness of &lt;code&gt;condition&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Courageous, but not always as intuitive as you’d expect. Time after time I shoot myself in the foot with JSX conditionals. In this article, I look at the trickier corners of JSX conditionals, and share some tips for staying safe:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Number &lt;code&gt;0&lt;/code&gt; likes to leak into your markup.&lt;/li&gt;
&lt;li&gt;Compound conditions with &lt;code&gt;||&lt;/code&gt; can surprise you because precedence&lt;/li&gt;
&lt;li&gt;Ternaries don’t scale.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;props.children&lt;/code&gt; is not something to use as a condition&lt;/li&gt;
&lt;li&gt;How to manage update vs remount in conditionals.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re in a hurry, I’ve made a cheat sheet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--73C5zOJo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/jsx-conditional-cheatsheet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--73C5zOJo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/jsx-conditional-cheatsheet.png" alt="" width="780" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Beware of zero
&lt;/h2&gt;

&lt;p&gt;Rendering on &lt;em&gt;numerical&lt;/em&gt; condition is a common use case. It’s helpful for rendering a collection only if it’s loaded and non-empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Gallery&lt;/span&gt; &lt;span class="na"&gt;slides&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if the gallery &lt;em&gt;is&lt;/em&gt; empty, we get an annoying &lt;code&gt;0&lt;/code&gt; in out DOM instead of nothing. That’s because of the way &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; works: a falsy left-hand side (like 0) is returned immediately. In JS, boolean operators do not cast their result to boolean — and for the better, since you don’t want the right-hand JSX to turn into &lt;code&gt;true&lt;/code&gt;. React then proceeds to put that 0 into the DOM — unlike &lt;code&gt;false&lt;/code&gt;, it’s a valid react node (again, for good — in &lt;code&gt;you have {count} tickets&lt;/code&gt; rendering 0 is perfectly expected).&lt;/p&gt;

&lt;p&gt;The fix? I have two. Cast the condition to boolean explicitly in any way you like. Now the expression value is &lt;code&gt;false&lt;/code&gt;, not &lt;code&gt;0&lt;/code&gt;, and &lt;code&gt;false&lt;/code&gt; is not rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;jsx&lt;/span&gt;
&lt;span class="c1"&gt;// or&lt;/span&gt;
&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;jsx&lt;/span&gt;
&lt;span class="c1"&gt;// or&lt;/span&gt;
&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;jsx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, replace &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; with a ternary to explicitly provide the falsy value — &lt;code&gt;null&lt;/code&gt; works like a charm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Gallery&lt;/span&gt; &lt;span class="na"&gt;slides&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mind the precedence
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;And&lt;/em&gt; (&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;) has a higher precedence than &lt;em&gt;or&lt;/em&gt; (&lt;code&gt;||&lt;/code&gt;) — that’s how &lt;a href="https://en.wikipedia.org/wiki/Boolean_algebra"&gt;boolean algebra&lt;/a&gt; works. However, this also means that you must be very careful with JSX conditions that contain &lt;code&gt;||&lt;/code&gt;. Watch as I try to render an access error for anonymous &lt;em&gt;or&lt;/em&gt; restricted users…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anonymous&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restricted&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;… and I screw up! The code above is actually equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anonymous&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restricted&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is &lt;em&gt;not&lt;/em&gt; what I want. For anonymous users, you get &lt;code&gt;true || ...whatever...&lt;/code&gt;, which is &lt;code&gt;true&lt;/code&gt;, because JS &lt;em&gt;knows&lt;/em&gt; the or-expression is true just by looking at the left-hand side and skips (&lt;a href="https://stackoverflow.com/questions/12554578/does-javascript-have-short-circuit-evaluation"&gt;short-circuits&lt;/a&gt;) the rest. React doesn’t render &lt;code&gt;true&lt;/code&gt;, and even if it did, &lt;code&gt;true&lt;/code&gt; is not the error message you expect.&lt;/p&gt;

&lt;p&gt;As a rule of thumb, parenthesize the condition as soon as you see the OR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anonymous&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restricted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a more sneaky case, consider this ternary-inside-the-condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registered&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restricted&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rateLimited&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parentheses still help, but avoiding ternaries in conditions is a better option — they’re very confusing, because you can’t even read the expression out in English (if the user is registered then if the user is restricted otherwise if the user is rate-limited, please make it stop).&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t get stuck in ternaries
&lt;/h2&gt;

&lt;p&gt;A ternary is a fine way to switch between &lt;em&gt;two&lt;/em&gt; pieces of JSX. Once you go beyond 2 items, the lack of an &lt;code&gt;else if ()&lt;/code&gt; turns your logic into a bloody mess real quick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isEmoji&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EmojiButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; 
  &lt;span class="nx"&gt;isCoupon&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CouponButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="nx"&gt;isLoaded&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ShareButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any extra conditions inside a ternary branch, be it a nested ternary or a simple &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, are a red flag. Sometimes, a series of &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; blocks works better, at the expense of duplicating some conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isEmoji&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EmojiButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isCoupon&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CouponButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isEmoji&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isCoupon&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;isLoaded&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ShareButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other times, a good old &lt;code&gt;if / else&lt;/code&gt; is the way to go. Sure, you can’t inline these in JSX, but you can always extract a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isEmoji&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EmojiButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt; 
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isCoupon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CouponButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isLoaded&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ShareButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Don’t conditional on JSX
&lt;/h2&gt;

&lt;p&gt;In case you’re wondering, react elements passed via props don’t work as a condition. Let me try wrapping the children in a div &lt;em&gt;only&lt;/em&gt; if there are children:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Wrap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I expect &lt;code&gt;Wrap&lt;/code&gt; to render &lt;code&gt;null&lt;/code&gt; when there is no content wrapped, but React doesn’t work like that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;props.children&lt;/code&gt; can be an empty array, e.g. &lt;code&gt;&amp;lt;Wrap&amp;gt;{[].map(e =&amp;gt; &amp;lt;div /&amp;gt;)}&amp;lt;/Wrap&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;children.length&lt;/code&gt; fails, too: &lt;code&gt;children&lt;/code&gt; can &lt;em&gt;also&lt;/em&gt; be a single element, not an array (&lt;code&gt;&amp;lt;Wrap&amp;gt;&amp;lt;div /&amp;gt;&amp;lt;/Wrap&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;React.Children.count(props.children)&lt;/code&gt; &lt;a href="https://reactjs.org/docs/react-api.html#reactchildrencount"&gt;supports&lt;/a&gt; both single and multiple children, but thinks that &lt;code&gt;&amp;lt;Wrap&amp;gt;{false &amp;amp;&amp;amp; 'hi'}{false &amp;amp;&amp;amp; 'there'}&amp;lt;/Wrap&amp;gt;&lt;/code&gt; contains 2 items, while in reality there are none.&lt;/li&gt;
&lt;li&gt;Next try: &lt;code&gt;React.Children.toArray(props.children)&lt;/code&gt; &lt;a href="https://reactjs.org/docs/react-api.html#reactchildrentoarray"&gt;removes invalid nodes,&lt;/a&gt; such as &lt;code&gt;false&lt;/code&gt;. Sadly, you still get true for an empty fragment: &lt;code&gt;&amp;lt;Wrap&amp;gt;&amp;lt;&amp;gt;&amp;lt;/&amp;gt;&amp;lt;Wrap&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For the final nail in this coffin, if we move the conditional rendering inside a component: &lt;code&gt;&amp;lt;Wrap&amp;gt;&amp;lt;Div hide /&amp;gt;&amp;lt;/Wrap&amp;gt;&lt;/code&gt; with &lt;code&gt;Div = (p) =&amp;gt; p.hide ? null : &amp;lt;div /&amp;gt;&lt;/code&gt;, we can &lt;em&gt;never&lt;/em&gt; know if it’s empty during &lt;code&gt;Wrap&lt;/code&gt; render, because react only renders the child &lt;code&gt;Div&lt;/code&gt; after the parent, and a stateful child can re-render independently from its parent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the only sane way to change anything if the interpolated JSX is empty, see &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:empty"&gt;CSS &lt;code&gt;:empty&lt;/code&gt; pseudo-class.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Remount or update?
&lt;/h2&gt;

&lt;p&gt;JSX written in separate ternary branches feels like completely independent code. Consider the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hasItem&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens when &lt;code&gt;hasItem&lt;/code&gt; changes? Don’t know about you, but my guess would be that &lt;code&gt;&amp;lt;Item id={1} /&amp;gt;&lt;/code&gt; unmounts, then &lt;code&gt;&amp;lt;Item id={2} /&amp;gt;&lt;/code&gt; mounts, because I wrote 2 separate JSX tags. React, however, doesn’t know or care what I wrote, all it sees is the &lt;code&gt;Item&lt;/code&gt; element in the same position, so it keeps the mounted instance, updating props (see &lt;a href="https://codesandbox.io/s/still-cherry-o123c?file=/src/App.js"&gt;sandbox&lt;/a&gt;). The code above is equivalent to &lt;code&gt;&amp;lt;Item id={hasItem ? 1 : 2} /&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When the branches contain different components, as in &lt;code&gt;{hasItem ? &amp;lt;Item1 /&amp;gt; : &amp;lt;Item2 /&amp;gt;}&lt;/code&gt;, React remounts, because &lt;code&gt;Item1&lt;/code&gt; can’t be updated to become &lt;code&gt;Item2&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The case above just causes some unexpected behavior that’s fine as long as you properly manage updates, and even a bit more optimal than remounting. However, with uncontrolled inputs you’re in for a disaster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"phone"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, if you input something into &lt;em&gt;name&lt;/em&gt; input, then switch the mode, your name unexpectedly leaks into the &lt;em&gt;phone&lt;/em&gt; input. (again, see &lt;a href="https://codesandbox.io/s/still-cherry-o123c?file=/src/App.js"&gt;sandbox&lt;/a&gt;) This can cause even more havoc with complex update mechanics relying on previous state.&lt;/p&gt;

&lt;p&gt;One workaround here is using the &lt;code&gt;key&lt;/code&gt; prop. Normally, we use it for &lt;a href="https://reactjs.org/docs/lists-and-keys.html"&gt;rendering lists,&lt;/a&gt; but it’s actually an &lt;em&gt;element identity&lt;/em&gt; hint for React — elements with the same &lt;code&gt;key&lt;/code&gt; are the same logical element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// remounts on change&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"phone"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"phone"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another option is replacing the ternary with two separate &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; blocks. When &lt;code&gt;key&lt;/code&gt; is absent, React falls back to the index of the item in &lt;code&gt;children&lt;/code&gt; array, so putting distinct elements into distinct positions works just as well as an explicit key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"phone"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conversely, if you have &lt;em&gt;very different&lt;/em&gt; conditional props on the same logical element, you can split the branching into two separate JSX tags for readability with &lt;em&gt;no&lt;/em&gt; penalty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// messy&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; 
  &lt;span class="na"&gt;aria-busy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
  &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Spinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// maybe try:&lt;/span&gt;
&lt;span class="nx"&gt;loading&lt;/span&gt; 
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;aria-busy&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Spinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// or even&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;aria-busy&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Spinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ^^ bonus: _move_ the element around the markup, no remount&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;So, here are my top tips for using JSX conditionals like a boss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{number &amp;amp;&amp;amp; &amp;lt;JSX /&amp;gt;}&lt;/code&gt; renders &lt;code&gt;0&lt;/code&gt; instead of nothing. Use &lt;code&gt;{number &amp;gt; 0 &amp;amp;&amp;amp; &amp;lt;JSX /&amp;gt;}&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;Don’t forget the parentheses around or-conditions: &lt;code&gt;{(cond1 || cond2) &amp;amp;&amp;amp; &amp;lt;JSX /&amp;gt;}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ternaries don’t scale beyond 2 branches — try an &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; block per branch, or extract a function and use &lt;code&gt;if / else&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can’t tell if &lt;code&gt;props.children&lt;/code&gt; (or any interpolated element) actually contains some content — CSS &lt;code&gt;:empty&lt;/code&gt; is your best bet.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{condition ? &amp;lt;Tag props1 /&amp;gt; : &amp;lt;Tag props2 /&amp;gt;}&lt;/code&gt; will &lt;em&gt;not&lt;/em&gt; remount &lt;code&gt;Tag&lt;/code&gt; — use unique &lt;code&gt;key&lt;/code&gt; or separate &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; branches if you want the remount.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>frontend</category>
      <category>react</category>
      <category>jsx</category>
    </item>
    <item>
      <title>Make useRef lazy — 4 ways</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Tue, 30 Nov 2021 11:52:33 +0000</pubDate>
      <link>https://forem.com/thoughtspile/make-useref-lazy-4-ways-2jdl</link>
      <guid>https://forem.com/thoughtspile/make-useref-lazy-4-ways-2jdl</guid>
      <description>&lt;p&gt;I love &lt;code&gt;useRef&lt;/code&gt;, but it lacks the lazy initializer functionality found in other hooks (&lt;code&gt;useState&lt;/code&gt; / &lt;code&gt;useReducer&lt;/code&gt; / &lt;code&gt;useMemo&lt;/code&gt;). &lt;code&gt;useRef({ x: 0, y: 0 })&lt;/code&gt; creates an object &lt;code&gt;{ x: 0, y: 0 }&lt;/code&gt; on every render, but only uses it when mounting — it subsequent renders it’s thrown away. With &lt;code&gt;useState&lt;/code&gt;, we can replace the initial &lt;em&gt;value&lt;/em&gt; with an &lt;em&gt;initializer&lt;/em&gt; that’s only called on first render — &lt;code&gt;useState(() =&amp;gt; ({ x: 0, y: 0 }))&lt;/code&gt; (I’ve explored this and other &lt;code&gt;useState&lt;/code&gt; features in my &lt;a href="https://thoughtspile.github.io/2021/09/27/usestate-tricks/"&gt;older post&lt;/a&gt;). Creating functions is very cheap in modern JS runtimes, so we skip allocating memory and building the object for a slight performance boost.&lt;/p&gt;

&lt;p&gt;I’m not super excited about doing useless work, and &lt;code&gt;useRef&lt;/code&gt; is your primary tool for &lt;a href="https://thoughtspile.github.io/2021/10/18/non-react-state/"&gt;avoiding useless re-renders.&lt;/a&gt; In this post, I’ll show you four ways to support lazy initializer in &lt;code&gt;useRef&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move initialization to &lt;code&gt;useEffect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sync lazy &lt;code&gt;useRef&lt;/code&gt; initializer that works like &lt;code&gt;useState&lt;/code&gt; initializer.&lt;/li&gt;
&lt;li&gt;Lazy &lt;code&gt;useRef&lt;/code&gt; on top of &lt;code&gt;useState&lt;/code&gt; (almost zero code!)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;useRef&lt;/code&gt; that only computes the value when you read &lt;code&gt;.current&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8ji2Yfh0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/lazy-useref-sofa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8ji2Yfh0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/lazy-useref-sofa.jpg" alt="" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;p&gt;Any ref that involves an object can benefit from lazy initialization. I use such refs a lot for &lt;a href="https://thoughtspile.github.io/2021/10/18/non-react-state/"&gt;tracking gestures:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;touch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onTouchMove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;touch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A lazy initializer is useless for atomic values like &lt;code&gt;useRef(9)&lt;/code&gt;, since those are cheap to create, too.&lt;/p&gt;

&lt;p&gt;For a slightly different use case, sometimes we want a stateful object (often a Resize/IntersectionObserver) with a stable identity — &lt;code&gt;useMemo&lt;/code&gt; &lt;a href="https://reactjs.org/docs/hooks-reference.html#usememo"&gt;does not guarantee it.&lt;/a&gt; We don’t really want to reassign &lt;code&gt;current&lt;/code&gt;, so a &lt;code&gt;RefObject&lt;/code&gt; API is not needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Would be nice&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useStableMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="c1"&gt;// Why write observer.current if you never swap an observer?&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each technique, we’ll see how good it is at supporting both use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  The async way
&lt;/h2&gt;

&lt;p&gt;The most intuitive way to lazy-initialize a ref is combining a value-less &lt;code&gt;useRef()&lt;/code&gt; with a mount effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nicely, init inside an effect does not (&lt;a href="https://thoughtspile.github.io/2021/11/15/unintentional-layout-effect/"&gt;normally&lt;/a&gt;) block the paint, allowing you to paint a touch faster. However, this implementation is not always convenient, because the &lt;code&gt;.current&lt;/code&gt; value is not accessible before the effect — in the first render phase, in DOM refs, &lt;code&gt;useLayoutEffect&lt;/code&gt;, and even in some other &lt;code&gt;useEffect&lt;/code&gt;s (inside child components and ones scheduled before the &lt;em&gt;init&lt;/em&gt; effect) — try it yourself in a &lt;a href="https://codepen.io/thoughtspile/pen/wvrvQjN?editors=0011"&gt;codepen&lt;/a&gt;. If the whole &lt;code&gt;useRef&lt;/code&gt; + &lt;code&gt;useEffect&lt;/code&gt; construction is written inline in a component, you at least see that the initialization is delayed. Wrapping it into a custom hook increases the chances of a misuse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLazyRef&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(...));&lt;/span&gt;
&lt;span class="c1"&gt;// spot the bug&lt;/span&gt;
&lt;span class="nx"&gt;useLayoutEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic relying on &lt;code&gt;.current&lt;/code&gt; is awkwardly pushed into effects, complicating your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLazyRef&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ResizeObserver&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;borderBoxSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data-width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replacing &lt;code&gt;useEffect&lt;/code&gt; with &lt;code&gt;useLayoutEffect&lt;/code&gt; does not help much — a bunch of places that can’t access the &lt;code&gt;current&lt;/code&gt; still exists (first render, DOM refs, child &lt;code&gt;useLayoutEffect&lt;/code&gt;s), &lt;em&gt;and&lt;/em&gt; now the initialization blocks the paint. As we’ll see now, better ways to initialize early exist.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;useEffect&lt;/code&gt; approach works OK if you only need &lt;code&gt;.current&lt;/code&gt; &lt;em&gt;later&lt;/em&gt; — in other effects, timeouts or event handlers (and you’re 100% sure those won’t fire during the first paint). It’s my least favorite approach, because the other ones work better and avoid the “pre-initialization gap”.&lt;/p&gt;

&lt;h2&gt;
  
  
  The DIY way
&lt;/h2&gt;

&lt;p&gt;If we want the &lt;code&gt;.current&lt;/code&gt; value to be available at all times, but without re-creation on every render (a lot like &lt;code&gt;useState&lt;/code&gt; / &lt;code&gt;useMemo&lt;/code&gt;), we can just build a custom hook over bare &lt;code&gt;useRef&lt;/code&gt; ourselves (see &lt;a href="https://codepen.io/thoughtspile/pen/MWEWzMe?editors=0011"&gt;codepen&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// none is a special value used to detect an uninitialized ref&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useLazyRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// not initialized yet&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// if it's not initialized (1st render)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// we initialize it&lt;/span&gt;
    &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// new we return the initialized ref&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation is a good default for custom &lt;code&gt;useLazyRef&lt;/code&gt; hooks: it works &lt;em&gt;anywhere&lt;/em&gt; — inside render, in effects and layout effects, in listeners, with no chance of misuse, and is similar to the built-in &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useMemo&lt;/code&gt;. To turn it into a readonly ref / stable memo, just return &lt;code&gt;ref.current&lt;/code&gt; — it’s already initialized before &lt;code&gt;useLazyRef&lt;/code&gt; returns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that using &lt;code&gt;null&lt;/code&gt; as the un-initialized value breaks if &lt;code&gt;init()&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt;, and setting &lt;code&gt;ref.current = null&lt;/code&gt; triggers an accidental re-initialization on next render. &lt;code&gt;Symbol&lt;/code&gt; works well and might be more convenient for debugging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the most convenient approach for storing &lt;code&gt;observers&lt;/code&gt;, because they’re safe to use from DOM refs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLazyRef&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ResizeObserver&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;borderBoxSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodeRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;nodeRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data-width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only downside is that the initializer runs even if we never read the value. I’ll show you how to avoid this, but first let’s see how we can (and can’t) build &lt;em&gt;this&lt;/em&gt; flavor of lazy &lt;code&gt;useRef&lt;/code&gt; over other hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The resourceful way
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;useState&lt;/code&gt; has the lazy initializer feature we want, why not just use it instead of writing custom code (&lt;a href="https://codepen.io/thoughtspile/pen/eYGYbYg?editors=0011"&gt;codepen&lt;/a&gt;)?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}))[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We &lt;code&gt;useState&lt;/code&gt; with a lazy initializer that mimics the shape of a RefObject, and throw away the update handle because we’ll never use it — ref identity must be stable. For readonly ref / stable-memo we can skip the &lt;code&gt;{ current }&lt;/code&gt; trick and just &lt;code&gt;useState(init)[0]&lt;/code&gt;. Storing a mutable object in &lt;code&gt;useState&lt;/code&gt; is not the most orthodox thing to do, but it works pretty well here. I imagine that at some point future react &lt;em&gt;might&lt;/em&gt; choose to rebuild the current &lt;code&gt;useState&lt;/code&gt; by re-initializing and re-applying all the updates (e.g. for HMR), but I haven’t heard of such plans, and this will break a lot of stuff.&lt;/p&gt;

&lt;p&gt;As usual, anything doable with &lt;code&gt;useState&lt;/code&gt; can also be done with &lt;code&gt;useReducer&lt;/code&gt;, but it’s slightly more complicated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="c1"&gt;// any reducer works, it never runs anyways&lt;/span&gt;
  &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="c1"&gt;// () =&amp;gt; {} and () =&amp;gt; 9 work just as well&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}))[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="c1"&gt;// And here's the stable memo:&lt;/span&gt;
&lt;span class="nx"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most obvious base hook, &lt;code&gt;useMemo&lt;/code&gt;, doesn’t work well. &lt;code&gt;useMemo(() =&amp;gt; ({ current: init() }), [])&lt;/code&gt; currently returns a stable object, but React docs &lt;a href="https://reactjs.org/docs/hooks-reference.html#usememo"&gt;warn against relying&lt;/a&gt; on this, since a future React version might re-initialize the value when it feels like it. If you’re OK with that, you didn’t need &lt;code&gt;ref&lt;/code&gt; in the first place.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useImperativeHandle&lt;/code&gt; is not recommended, too — it &lt;a href="https://reactjs.org/docs/hooks-reference.html#useimperativehandle"&gt;has something to do with refs,&lt;/a&gt; but its &lt;a href="https://github.com/facebook/react/blob/82c8fa90be86fc0afcbff2dc39486579cff1ac9a/packages/react-reconciler/src/ReactFiberHooks.new.js#L1777"&gt;implemented&lt;/a&gt; to set the value in a layout effect, similar to the worst one of our &lt;code&gt;async&lt;/code&gt; options. Also, it&lt;/p&gt;

&lt;p&gt;So, &lt;code&gt;useState&lt;/code&gt; allows you to build a &lt;em&gt;lazy ref&lt;/em&gt; with almost zero code, at a minor risk of breaking in a future react version. Choosing between this and a DIY lazy ref is up to you, they work the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  The really lazy way
&lt;/h2&gt;

&lt;p&gt;I’d argue that what we’ve discussed so far isn’t really &lt;em&gt;lazy&lt;/em&gt; — sure, you avoid useless job on re-render, but you still eagerly compute the initial value on first render. What if we only computed the value on demand, when someone reads &lt;code&gt;.current&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useJitRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLazyRef&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tricky! See &lt;a href="https://codepen.io/thoughtspile/pen/YzrzdyJ?editors=0011"&gt;codepen&lt;/a&gt;, and let me break it down for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrap the bare ref with a get / set interceptor&lt;/li&gt;
&lt;li&gt;Reading &lt;code&gt;current&lt;/code&gt; goes through the &lt;code&gt;get()&lt;/code&gt;, computing the value on first read and returning the cached value later.&lt;/li&gt;
&lt;li&gt;Assigning &lt;code&gt;current&lt;/code&gt; updates the value instantly and removes the need to initialize.&lt;/li&gt;
&lt;li&gt;The wrapper object is a &lt;code&gt;useLazyRef&lt;/code&gt; itself to preserve the builtin &lt;code&gt;useRef&lt;/code&gt; guarantee of stable identity and avoid extra object creation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For readonly ref / stable memo, try the simpler &lt;em&gt;getter function&lt;/em&gt; approach &lt;a href="https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily"&gt;suggested in react docs:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useMemoGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is it worth the trouble? Maybe, maybe not. The code is more complicated than the eager &lt;code&gt;useLazyRef&lt;/code&gt;. If the initializer is &lt;em&gt;really&lt;/em&gt; heavy, and you use the value conditionally, and you often end up not needing it, sure, it’s a good fit. Honestly, I have yet to see a use case that fits these conditions.&lt;/p&gt;

&lt;p&gt;This is a very interesting and flexible technique that supports many variations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-compute the value, e.g. in &lt;code&gt;requestIdleCallback(() =&amp;gt; ref.current)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Allow for lazy updates — don’t set the explicit value, but provide a new way to compute it: &lt;code&gt;ref.current = () =&amp;gt; el.clientWidth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace &lt;em&gt;updating&lt;/em&gt; with &lt;em&gt;invalidation&lt;/em&gt; — say, with &lt;code&gt;getWidth = useMemoGet(() =&amp;gt; el.clientWidth)&lt;/code&gt; you can mark the cached value as stale with &lt;code&gt;getWidth.invalidate()&lt;/code&gt; on content change.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;We’ve covered 4 good base techniques (&lt;code&gt;useState&lt;/code&gt; is an alternative implementation of ) for creating lazy useRef. They all have different characteristics that make them useful for different problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialize in &lt;code&gt;useEffect&lt;/code&gt; — not recommended because it’s easy to hit un-initialized &lt;code&gt;.current&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sync custom-built &lt;code&gt;useRef&lt;/code&gt; works well, but blocks first render. Good enough for most cases.&lt;/li&gt;
&lt;li&gt;Putting the value into &lt;code&gt;useState&lt;/code&gt;‘s initializer, but hiding the update handle. Least code, but a chance of breaking in future react versions.&lt;/li&gt;
&lt;li&gt;On-demand &lt;code&gt;useRef&lt;/code&gt; that only computes the value when you read &lt;code&gt;.current&lt;/code&gt; — complicated, but flexible and never computes values you don’t use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope you find this useful! If you want to learn more about react, check out my &lt;a href="https://thoughtspile.github.io/"&gt;other posts.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Open source starter pack for JS devs</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Sat, 20 Nov 2021 13:25:25 +0000</pubDate>
      <link>https://forem.com/thoughtspile/open-source-starter-pack-for-js-devs-420n</link>
      <guid>https://forem.com/thoughtspile/open-source-starter-pack-for-js-devs-420n</guid>
      <description>&lt;p&gt;So you’ve decided to open-source your project. Amazing! Bad news first: writing code is only the beginning. The information for library authors on the web is surprisingly fragmented, so I’ve decided to put together a list of things to keep in mind when open-sourcing a JS library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decent docs, an OSS license, TypeScript definitions and a changelog are a must if you want anyone to use your library.&lt;/li&gt;
&lt;li&gt;Build setup of a library is different from that of an app — but even simpler, if you know what to do.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;peerDependenies&lt;/code&gt; are a thing (also, npm / yarn version resolution is a nightmare).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll give you enough info on each point to get started without going too deep.&lt;/p&gt;

&lt;p&gt;I assume you’re comfortable with &lt;a href="https://babeljs.io/"&gt;babel&lt;/a&gt; and &lt;a href="https://webpack.js.org/"&gt;webpack&lt;/a&gt; (or any other build toolchain), and have followed some &lt;a href="https://www.digitalocean.com/community/tutorials/workflow-publishing-first-package-to-npm"&gt;basic tutorial&lt;/a&gt; on publishing an npm package. I also suggest you host your code on GitHub — it has a ton of features useful for OSS, and any other choice is just &lt;em&gt;bizarre&lt;/em&gt; in 2021. Let’s pick up where you’ve written some code, pushed it to a public github repo, set up your &lt;code&gt;package.json&lt;/code&gt; with &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt;, and got &lt;code&gt;npm publish&lt;/code&gt; to pass. There’s still a bumpy ride ahead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pXlOvlad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/oss-scream.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pXlOvlad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/oss-scream.jpg" alt="" width="880" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;Your library will be used by people who have no idea what it’s supposed to do, and how it works. For a small project, don’t block yourself into building a fancy docs website — a &lt;code&gt;readme.md&lt;/code&gt; file in the repo root is enough. Writing the actual docs is a better way to spend your time. Make sure to include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem does your project solve? Does your library do something I need?&lt;/li&gt;
&lt;li&gt;How to install it? Even if it’s &lt;code&gt;npm i my-project&lt;/code&gt;, don’t make me guess the package name.&lt;/li&gt;
&lt;li&gt;A basic usage example. Just something to copy-paste and get started with it.&lt;/li&gt;
&lt;li&gt;Full API docs. What can the library do? Does it cover all my use-cases? What arguments do I pass where? I shouldn’t have to read the source to answer these questions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See &lt;a href="https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax"&gt;markdown cheatsheet&lt;/a&gt; if you’re not fluent yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(I’m not a lawyer, but this is my understanding).&lt;/em&gt; A project without an explicit license is closed source by default — the users can look at the code, but can’t legally use it. &lt;a href="https://choosealicense.com/"&gt;Choose an open-source license,&lt;/a&gt; copy the text into a &lt;code&gt;LICENSE&lt;/code&gt; file to your repo, put the name in the &lt;code&gt;license&lt;/code&gt; field of &lt;code&gt;package.json&lt;/code&gt;, and probably mention it in the readme. &lt;a href="https://mit-license.org/"&gt;MIT license&lt;/a&gt; is a good choice if you just want everyone to use your code and don’t have a strong opinion on &lt;em&gt;everything must be open-source.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Package structure
&lt;/h2&gt;

&lt;p&gt;When building an app, you have some transpiler + bundler (probably babel + webpack, but anything goes) setup to turn your source into someting that runs in a browser. If you don’t want to make your users jump around patching webpack config, you need &lt;em&gt;some&lt;/em&gt; of that, too. Exactly &lt;em&gt;how&lt;/em&gt; you should package your code is a complex topic, but let me scratch the surface for you. TLDR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you use extended JS (JSX / TS / whatever), or want to support older runtimes with zero setup, do a &lt;code&gt;babel&lt;/code&gt; (or friends) pass.&lt;/li&gt;
&lt;li&gt;Have a ES-module build for tree-shaking, and a legacy CommonJS build.&lt;/li&gt;
&lt;li&gt;Be clear about supported browsers / node versions. Too much = bloat, too little = broken apps for users who don’t do extra setup (they won’t).&lt;/li&gt;
&lt;li&gt;Never use global polyfills, and prefer well-supported APIs when possible.&lt;/li&gt;
&lt;li&gt;Don’t bother bundling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just a touch deeper:&lt;/p&gt;

&lt;h3&gt;
  
  
  Transpiling
&lt;/h3&gt;

&lt;p&gt;The code you ship &lt;em&gt;must&lt;/em&gt; be standard ES to “just work” for your users. Convert JSX / TypeScript / Vue SFC / other fancy syntax down to JS with &lt;code&gt;babel&lt;/code&gt;, &lt;code&gt;tsc&lt;/code&gt;, or whatever &lt;code&gt;esbuild&lt;/code&gt; you enjoy, and point &lt;code&gt;main&lt;/code&gt; to the built version instead of your raw source.&lt;/p&gt;

&lt;p&gt;The exact ES target (ES2020 / 6 / 5) is up to you — pick a browser / node target and stick to it. Your users &lt;em&gt;can&lt;/em&gt; transpile further down, if they’re determined, but undoing an unnecessary transform is next to impossible — it becomes bloat in the final app. Also, some babel transforms (like &lt;a href="https://babeljs.io/docs/en/babel-plugin-transform-unicode-regex"&gt;unicode regex&lt;/a&gt;) are &lt;em&gt;very&lt;/em&gt; verbose — have a look at the generated code once in a while.&lt;/p&gt;

&lt;p&gt;The most important question is what to do with &lt;code&gt;import / export&lt;/code&gt;. Read on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tree-shaking
&lt;/h3&gt;

&lt;p&gt;If your library runs in a browser, and it does more than &lt;em&gt;one&lt;/em&gt; thing, you’d better support &lt;a href="https://webpack.js.org/guides/tree-shaking/"&gt;tree shaking&lt;/a&gt; — otherwise, every app using your library will ship useless dead code to the end users’ browsers. To get started, create a modular build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;modules: false&lt;/code&gt; in &lt;a href="https://babeljs.io/docs/en/babel-preset-env#modules"&gt;&lt;code&gt;@babel/preset-env&lt;/code&gt; config&lt;/a&gt; to preserve &lt;code&gt;import / export&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;"sideEffects": false&lt;/code&gt; or &lt;a href="https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free"&gt;list your side-effect modules&lt;/a&gt; explicitly in &lt;code&gt;package.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Point the &lt;a href="https://webpack.js.org/guides/author-libraries/#final-steps"&gt;non-standard &lt;code&gt;module&lt;/code&gt;&lt;/a&gt; field of &lt;code&gt;package.json&lt;/code&gt; to the resulting entrypoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now a module-capable bundler can pick it up and remove unused code from the final bundle. Cool.&lt;/p&gt;

&lt;p&gt;However, node &amp;lt;= 12 does not support import / export syntax &lt;em&gt;without a flag&lt;/em&gt;. I’m lost in all the &lt;a href="https://nodejs.org/api/esm.html"&gt;new &lt;code&gt;"type": "module"&lt;/code&gt; / &lt;code&gt;".mjs"&lt;/code&gt;&lt;/a&gt;, but shipping a &lt;a href="https://web.dev/publish-modern-javascript/#modern-with-legacy-fallback"&gt;fallback CommonJS build&lt;/a&gt; works fine in all node versions and older bundlers. For a library that works both client- and server-side, keep a CommonJS (&lt;code&gt;exports / require&lt;/code&gt;) version (generated with a &lt;a href="https://babeljs.io/docs/en/babel-preset-env#modules"&gt;pass of babel&lt;/a&gt; with &lt;code&gt;modules: 'commonjs'&lt;/code&gt;), referenced in &lt;code&gt;main&lt;/code&gt; field of &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There’s more to tree shaking — some patterns are not tree-shakable, it affects your API choices, complex topic. We’ll save for it later — supporting basic tree-shaking with ES modules is always a good start.&lt;/p&gt;

&lt;h3&gt;
  
  
  Polyfills
&lt;/h3&gt;

&lt;p&gt;Should your library include the polyfills for recent browser APIs you use? It’s a &lt;a href="https://github.com/w3ctag/polyfills/issues/6"&gt;surprisingly debatable issue&lt;/a&gt; among OSS authors. Problem, short version: in most app setups, &lt;code&gt;babel&lt;/code&gt; doesn’t process &lt;code&gt;node_modules&lt;/code&gt;, possibly breaking the final app in older browsers. &lt;em&gt;But&lt;/em&gt; if you include a polyfill, removing it is from an app that only targets modern browsers is super hard. Also, the final bundle is likely to contain duplicate polyfills of one API.&lt;/p&gt;

&lt;p&gt;I’ll stick with &lt;a href="https://github.com/w3ctag/polyfills/issues/6#issuecomment-272222513"&gt;Rich&lt;/a&gt; &lt;a href="https://github.com/w3ctag/polyfills/issues/6#issuecomment-272651240"&gt;Harris&lt;/a&gt; on this one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Never&lt;/em&gt; include global polyfills that patch &lt;code&gt;BuiltIn.prototype&lt;/code&gt; or &lt;code&gt;window&lt;/code&gt; — they can clash with other global polyfills, and are not tree-shakable.&lt;/li&gt;
&lt;li&gt;Use helper functions (aka &lt;a href="https://ponyfill.com/"&gt;&lt;em&gt;ponyfills&lt;/em&gt;&lt;/a&gt;) like &lt;code&gt;export function startsWith&lt;/code&gt; if for code reuse.&lt;/li&gt;
&lt;li&gt;Prefer well-supported APIs if possible — using &lt;code&gt;str.indexOf(...) === 0&lt;/code&gt; instead of &lt;code&gt;startsWith&lt;/code&gt; is not &lt;em&gt;that&lt;/em&gt; hard.&lt;/li&gt;
&lt;li&gt;Clearly say what targets you support in the readme. Don’t pretend to support IE11 if you’re not very serious. Maybe provide instructions on setting up &lt;code&gt;babel-loader&lt;/code&gt; to process your library.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bundling
&lt;/h3&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; bundle your code, but I think it creates more problems than it solves at the start. How to exclude your dependencies from the bundle? What &lt;code&gt;libraryTarget&lt;/code&gt; do you need? Are we sure bundling does not accidentally create non-tree-shakable logic? I’d stick with babel CLI and ship the code as separate JS files: &lt;code&gt;npx babel src --out-dir dist&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typings
&lt;/h2&gt;

&lt;p&gt;TypeScript is a major player in the JS ecosystem these days. Libraries without TS types explode in TS projects with &lt;code&gt;Could not find a declaration file for module '...'&lt;/code&gt;, forcing users to either &lt;code&gt;@ts-ignore&lt;/code&gt; it or slap together custom ambient declarations. Some lazier developers will probably move to the next library, and I won’t blame them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ePmuUd8X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/no-dts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ePmuUd8X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/no-dts.png" alt="" width="880" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shipping TS types is actually easy: if you write in TypeScript, &lt;code&gt;tsc --declaration&lt;/code&gt; (&lt;code&gt;--declarationOnly&lt;/code&gt; if you build with babel, see &lt;a href="https://www.typescriptlang.org/tsconfig#declaration"&gt;docs&lt;/a&gt;) into your build folder. If you write pure JS, it’s even easier — just write a custom &lt;code&gt;index.d.ts&lt;/code&gt; file describing your library, and copy it to build folder. Now point &lt;code&gt;types&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; to the declaration entry point, and you’re all set! Don’t worry about &lt;code&gt;@types/*&lt;/code&gt; pacakges for now. See full &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#including-declarations-in-your-npm-package"&gt;TS docs on publishing&lt;/a&gt; if you have any trouble.&lt;/p&gt;

&lt;p&gt;I don’t know much about Flow, and no one ever asked me to support it, but if you’re a fan, see &lt;a href="https://stackoverflow.com/questions/61889988/exporting-my-own-flow-type-with-npm-package"&gt;SO tips on doing that.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand dependencies vs dev/peerDependencies
&lt;/h2&gt;

&lt;p&gt;In app development &lt;code&gt;dependencies&lt;/code&gt; (&lt;code&gt;npm i&lt;/code&gt;) vs &lt;code&gt;devDependenices&lt;/code&gt; (&lt;code&gt;npm i --dev&lt;/code&gt;) is not a real issue — sure, &lt;code&gt;dev&lt;/code&gt; is for your build pipeline, &lt;code&gt;dependencies&lt;/code&gt; for real runtime libraries, but it mostly works fine if you mess up. The difference is critical for libraries, though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;devDependencies&lt;/code&gt; are not installed after &lt;code&gt;npm i your-lib&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dependencies&lt;/code&gt; are automatically installed along with your package. If the user (or some other package) requests a different version of the same dependency, they may get duplicated, but at least it usually works.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;peerDependencies&lt;/code&gt; allow you to reference a package explicitly installed by your users. This ensures the dependency instance is shared between user code and your library, which is crucial for &lt;em&gt;plugins&lt;/em&gt; — react components, express middlewares, etc. In effect, this forces a single dependency version per app — your users can’t upgrade to react 18 until you support it. Assume the users have to install peers manually — sure, npm 7+ installs &lt;a href="https://github.blog/2021-02-02-npm-7-is-now-generally-available/#peer-dependencies"&gt;them automatically&lt;/a&gt;, but &lt;a href="https://github.com/yarnpkg/yarn/issues/1503"&gt;yarn&lt;/a&gt; and &lt;a href="https://github.com/pnpm/pnpm/issues/827"&gt;pnpm&lt;/a&gt; don’t. People hate manually installing stuff they don’t care about to get the project to build, so don’t overuse peers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bundleddependencies"&gt;bundledDependencies&lt;/a&gt; and &lt;a href="https://docs.npmjs.com/cli/v8/configuring-npm/package-json#optionaldependencies"&gt;optionalDependencies?&lt;/a&gt; You don’t need them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basic guideline: all the runtime dependencies go into &lt;code&gt;dependencies&lt;/code&gt;. &lt;em&gt;Plugins&lt;/em&gt; should put the main library into &lt;code&gt;peerDependencies&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changelog
&lt;/h2&gt;

&lt;p&gt;I install your library, and I’m happy with it. Some time later, it’s friday evening and I can’t get real job done any more. I decide to update my dependencies, and discover that your library moved from &lt;code&gt;1.0.3&lt;/code&gt; to &lt;code&gt;2.3.0&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have you fixed some important bugs, so that I need to update &lt;strong&gt;right now&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Have you added new features I might enjoy?&lt;/li&gt;
&lt;li&gt;What’s the breaking change in v2, and how do I update?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To answer these questions, I’d love to see a changelog saying what changed in every version since &lt;code&gt;1.0.3&lt;/code&gt;. Both &lt;a href="https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases"&gt;GH releases&lt;/a&gt; and a &lt;code&gt;CHANGELOG.md&lt;/code&gt; in the root work fine. Otherwise, I’ll have to read the commit / PR list, which is likely to make me very sad.&lt;/p&gt;




&lt;p&gt;So, here’s my list of stuff to keep in mind when publishing a JS library:&lt;/p&gt;

&lt;p&gt;Must have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A readme with a problem statement, installation command, hello world example, and full API docs.&lt;/li&gt;
&lt;li&gt;A license: full text in project root, name in the readme and in &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;TS typings (unless it’s a CLI tool).&lt;/li&gt;
&lt;li&gt;Changelog in GH releases or a &lt;code&gt;changelog.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Managing dependencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Runtime &lt;code&gt;dependencies&lt;/code&gt; go into &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Plugins&lt;/em&gt; make the &lt;em&gt;master library&lt;/em&gt; a &lt;code&gt;peerDependency&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Build setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transpile the code to standard JS.&lt;/li&gt;
&lt;li&gt;Running in browsers? Ship es-modules entrypoint in &lt;code&gt;package.json&lt;/code&gt; &lt;code&gt;modules&lt;/code&gt; for tree shaking.&lt;/li&gt;
&lt;li&gt;Running on node? Ship CommonJS entrypoint in &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Avoid global polyfills.&lt;/li&gt;
&lt;li&gt;Don’t bundle.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>useEffect sometimes fires before paint</title>
      <dc:creator>Vladimir Klepov</dc:creator>
      <pubDate>Mon, 15 Nov 2021 12:09:54 +0000</pubDate>
      <link>https://forem.com/thoughtspile/useeffect-sometimes-fires-before-paint-3o4h</link>
      <guid>https://forem.com/thoughtspile/useeffect-sometimes-fires-before-paint-3o4h</guid>
      <description>&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt; should run after paint to prevent blocking the update. But did you know it’s not really &lt;em&gt;guaranteed&lt;/em&gt; to fire after paint? Updating state in &lt;code&gt;useLayoutEffect&lt;/code&gt; makes every &lt;code&gt;useEffect&lt;/code&gt; from the same render run &lt;em&gt;before&lt;/em&gt; paint, effectively turning them into layout effects. Confusing? Let me explain.&lt;/p&gt;

&lt;p&gt;In a normal flow, react updates go like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;React stuff: render virtual DOM, schedule effects, update real DOM&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;useLayoutEffect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;React releases control, browser paints the new DOM&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;useEffect&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://reactjs.org/docs/hooks-reference.html#useeffect"&gt;React docs&lt;/a&gt; don’t say when, exactly, useEffect fires — it happens, quote, &lt;strong&gt;after layout and paint, during a deferred event.&lt;/strong&gt; I always assumed it was a &lt;code&gt;setTimeout(effect, 3)&lt;/code&gt;, but it &lt;a href="https://stackoverflow.com/a/56727837"&gt;appears to use&lt;/a&gt; a &lt;code&gt;MessageChannel&lt;/code&gt; trick, which is neat.&lt;/p&gt;

&lt;p&gt;There is, however, a more interesting passage in the docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Although useEffect is deferred until after the browser has painted, it’s guaranteed to fire before any new renders. React will always flush a previous render’s effects before starting a new update.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a good guarantee — you can be sure no updates are missed. But it also implies that sometimes the effect fires before paint. If &lt;em&gt;a)&lt;/em&gt; effects are flushed before a new update starts, and &lt;em&gt;b)&lt;/em&gt; an update can start &lt;em&gt;before&lt;/em&gt; paint, e.g. when triggered from &lt;code&gt;useLayoutEffect&lt;/code&gt;, &lt;em&gt;then&lt;/em&gt; the effect must be flushed &lt;em&gt;before&lt;/em&gt; that update, which is &lt;em&gt;before&lt;/em&gt; paint. Here’s a timeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SlspT0hN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/forced-le-flush-chart.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SlspT0hN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/forced-le-flush-chart.png" alt="" width="640" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;React update 1: render virtual DOM, schedule effects, update DOM&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;useLayoutEffect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update state,&lt;/strong&gt; schedule re-render&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Call &lt;code&gt;useEffect&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;React update 2&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;useLayoutEffect&lt;/code&gt; from update 2&lt;/li&gt;
&lt;li&gt;React releases control, browser &lt;strong&gt;paints&lt;/strong&gt; the new DOM&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;useEffect&lt;/code&gt; from update 2&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not a very rare situation — you can’t really update state in &lt;code&gt;useEffect&lt;/code&gt;, because updating state updates the DOM, and doing so after paint leaves the user with one stale frame, &lt;a href="https://blog.logrocket.com/useeffect-vs-uselayouteffect/"&gt;resulting in noticeable flickering.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, let’s build a responsive input (like a fake &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries"&gt;CSS container query&lt;/a&gt;) that only renders the clear button if the input is wider than &lt;code&gt;300px&lt;/code&gt;. We need real DOM to measure the input, so we need some effect. We also don’t want the icon to appear / disappear after one frame, so the initial measurement goes into &lt;code&gt;useLayoutEffect&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ResponsiveInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onClear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setW&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="nx"&gt;useLayoutEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt; 
  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;// don't take this too seriously, say it's a ResizeObserver &lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClear&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;clear&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve tried to delay &lt;code&gt;addEventListener&lt;/code&gt; until after paint with &lt;code&gt;useEffect&lt;/code&gt;, but the state update in &lt;code&gt;useLayoutEffect&lt;/code&gt; forces it to happen before paint &lt;a href="https://codesandbox.io/s/infallible-wildflower-127lv?file=/src/App.js:294-408"&gt;(see sandbox):&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4b0zslXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/le-flush-paint.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4b0zslXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thoughtspile.github.io/images/le-flush-paint.png" alt="" width="758" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, this is not the end of the world — under some circumstances, your render flow is less optimal than it could be, who cares. Still, it’s useful to know the limitations of your tool. Here are 4 practical lessons to learn:&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t rely on useEffect to fire after update
&lt;/h3&gt;

&lt;p&gt;Even if you know the catch, it’s very hard to make sure some &lt;code&gt;useEffect&lt;/code&gt; is not affected by &lt;code&gt;useLayoutEffect&lt;/code&gt; state update:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My components doesn’t &lt;code&gt;useLayoutEffect&lt;/code&gt;. But are you sure none of the custom hooks it uses do that?&lt;/li&gt;
&lt;li&gt;My components only uses built-in React hooks. But a uLE state update up the tree can leak through &lt;code&gt;useContext&lt;/code&gt; or a parent re-render.&lt;/li&gt;
&lt;li&gt;My components only has &lt;code&gt;useEffect&lt;/code&gt;, and a &lt;code&gt;memo()&lt;/code&gt;. But a uLE state update in the parent still appears to flush child effects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With a lot of discipline you probably can have a codebase with &lt;em&gt;no&lt;/em&gt; state updates in &lt;code&gt;useLayoutEffect&lt;/code&gt;, but that’s superhuman. The best advice is not to rely on &lt;code&gt;useEffect&lt;/code&gt; to fire after paint, just like &lt;code&gt;useMemo&lt;/code&gt; does not guarantee 100% stable reference. If you &lt;em&gt;want&lt;/em&gt; the user to see something painted for one frame, &lt;code&gt;useEffect&lt;/code&gt; is not the way to do it — try double &lt;code&gt;requestAnimationFrame&lt;/code&gt; or do the postMessage trick yourself.&lt;/p&gt;

&lt;p&gt;Conversely, suppose you don’t listen to the good advice from React team and update DOM in &lt;code&gt;useEffect&lt;/code&gt;. You test it, and, &lt;em&gt;aha!&lt;/em&gt;, no flickering. Bad news — maybe it’s the result of a state update before paint. Move some code around, and it &lt;em&gt;will&lt;/em&gt; flicker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t waste your time splitting layout effects
&lt;/h3&gt;

&lt;p&gt;Following &lt;code&gt;useEffect&lt;/code&gt; vs &lt;code&gt;useLayoutEffect&lt;/code&gt; guidelines to the letter, we could split one logical side-effect into a layout effect to update the DOM, and a “delayed” effect, like we’ve done in our &lt;code&gt;ResponsiveInput&lt;/code&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DOM update = layout effect&lt;/span&gt;
&lt;span class="nx"&gt;useLayoutEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="c1"&gt;// subscription = lazy logic&lt;/span&gt;
&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, as we now know, this does nothing — both effects are flushed before render. Besides, the separation is &lt;em&gt;sloppy&lt;/em&gt; — if we pretend &lt;code&gt;useEffect&lt;/code&gt; does fire after paint, are you 100% sure the element won’t resize between the effects? I’m not. Leaving all size-tracking logic in a single &lt;code&gt;layoutEffect&lt;/code&gt; here is safer, cleaner, has the same amount of pre-paint work, and gives React one less effect to manage — pure win:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;useLayoutEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Don’t update state in useLayoutEffect
&lt;/h3&gt;

&lt;p&gt;Good advice, but easier said than done — &lt;code&gt;useEffect&lt;/code&gt; is a worse place to update state, because flickering is poor UX, and UX is more important than performance. Updating state during render looks dangerous. If you can, try to come up with a &lt;a href="https://thoughtspile.github.io/2021/09/21/useeffect-derived-state/"&gt;state model that doesn’t rely on effects,&lt;/a&gt; but I don’t know how to invent “good” state models on command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bypass state update
&lt;/h3&gt;

&lt;p&gt;If you find particular &lt;code&gt;useLayoutEffect&lt;/code&gt; causing trouble, consider bypassing state update and mutating DOM directly. That way, react doesn’t schedule an update, and needn’t flush effects eagerly. We could try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="c1"&gt;// No worries react, I'll handle it:&lt;/span&gt;
  &lt;span class="nx"&gt;clearRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;useLayoutEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;clearRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClear&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;clear&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve explored this technique in my &lt;a href="https://thoughtspile.github.io/2021/10/18/non-react-state/"&gt;older post on avoiding &lt;code&gt;useState&lt;/code&gt;&lt;/a&gt;, and we just got one more reason to skip react updates. Still, manually managing DOM updates is complicated and error-prone, so reserve this trick for performance-critical situations — very hot components or super-heavy useEffects.&lt;/p&gt;




&lt;p&gt;Today we’ve discovered that &lt;code&gt;useEffect&lt;/code&gt; sometimes executes before paint. A frequent cause is updating state in &lt;code&gt;useLayoutEffect&lt;/code&gt; — it requests a re-render &lt;em&gt;before&lt;/em&gt; paint, and the effect must run before that re-render. What this means for us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Updating state in &lt;code&gt;useLayoutEffect&lt;/code&gt; is not good for app performance. Try not to do that, but sometimes there is no good alternative.&lt;/li&gt;
&lt;li&gt;Don’t rely on &lt;code&gt;useEffect&lt;/code&gt; to fire after paint.&lt;/li&gt;
&lt;li&gt;Updating DOM from &lt;code&gt;useEffect&lt;/code&gt; &lt;em&gt;will&lt;/em&gt; cause a visible flicker — maybe you don’t see it because of a layout effect updating state.&lt;/li&gt;
&lt;li&gt;Extracting a part of &lt;code&gt;useLayoutEffect&lt;/code&gt; into &lt;code&gt;useEffect&lt;/code&gt; for &lt;em&gt;performance&lt;/em&gt; makes no sense if you set state in the layout effect part.&lt;/li&gt;
&lt;li&gt;One more reason to mutate the DOM from uLE manually in performance-critical cases.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>frontend</category>
      <category>hooks</category>
    </item>
  </channel>
</rss>
