<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Marcel's Scribble Corner]]></title><description><![CDATA[Marcel's Scribble Corner]]></description><link>https://blog.mrcljx.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 24 Apr 2026 04:29:35 GMT</lastBuildDate><atom:link href="https://blog.mrcljx.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[NYC Tip: Sleep No More]]></title><description><![CDATA[While I had a short layover in New York last spring, I finally got to visit properly this year. During my trip planning, I reached out to Orta who had lived in NYC for a while. While he gave me a wonderful list of things to eat, visit, and do, one it...]]></description><link>https://blog.mrcljx.dev/sleep-no-more</link><guid isPermaLink="true">https://blog.mrcljx.dev/sleep-no-more</guid><category><![CDATA[Travel]]></category><category><![CDATA[sleep no more]]></category><category><![CDATA[immersive]]></category><category><![CDATA[Theatre]]></category><dc:creator><![CDATA[Marcel Jackwerth]]></dc:creator><pubDate>Tue, 29 Aug 2023 23:53:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-HDS8ys8Lbk/upload/74fff3649f24a7955ec72f75c2fadb41.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While I had a short layover in New York last spring, I finally got to visit properly this year. During my trip planning, I reached out to <a target="_blank" href="https://orta.io/">Orta</a> who had lived in NYC for a while. While he gave me a wonderful list of things to eat, visit, and do, one item caught my interest: an immersive performance called <a target="_blank" href="https://mckittrickhotel.com/events/sleep-no-more/">Sleep No More</a>.</p>
<blockquote>
<p><em>Sleep No More</em> is an award winning theatrical experience that tells Shakespeare’s Scottish tragedy, through a darkly cinematic lens. The story unfolds through an awe-inspiring blend of acrobatic choreography, film noir soundtrack, and countless rooms of densely detailed atmosphere. Guests decide where to go and what to see, ensuring that everyone’s journey is unique, and each visit is different.</p>
</blockquote>
<p>I researched it a tiny bit more and learned that it's best experienced without too much knowledge about what you'll experience – and I agree.</p>
<p>I was able to talk two other friends into going (not easy, as the price of admission is around $150) but that evening turned out to be the highlight of the trip - I even asked another friend and went again a few days later after digesting the first experience. We kept talking about it for days and it's still woven into my dreams to this day.</p>
<h2 id="heading-some-spoiler-free-tips">Some Spoiler-Free Tips</h2>
<ul>
<li>Read at least a five-sentence summary of Macbeth. But be aware that Macbeth is just a framework and theme for Sleep No More.</li>
</ul>
<ul>
<li><p>Consider taking one or two friends, but roam independently. You'll want to talk to someone after it.</p>
</li>
<li><p>Try to guess a slow night and book the earliest slot (e.g. 7pm). My suggestions would be Sunday to Wednesday.</p>
</li>
<li><p>Book “Oz’ Guest” (add $30) ticket for the expedited access line. It also will reserve you a seat a table at the jazz bar in lobby of the venue.</p>
</li>
<li><p>Be there 15 minutes before your slot to be one of the first in the expedited access line.</p>
</li>
<li><p>If you order a drink after you get to the lobby, be prepared that you might not get to drink it before being admitted into the experience (definitely go in as soon as you’re called). You might consider asking your waiter whether they can get you a water and whether you can pre-order something for after the show ends at 10pm. Consider leaving the alcohol for later to be alert for the experience.</p>
</li>
<li><p><strong>Optional:</strong> Consider wearing clothes that fit with the <em>dark noir</em> theme. But wear shoes that you can <em>run</em> in.</p>
</li>
</ul>
<p>A few things that might make sense once you're in:</p>
<ul>
<li><p>Be curious and accept that you will miss things (the FOMO is strong here).</p>
</li>
<li><p>Identifying some of the Macbeth characters will help you navigate.</p>
</li>
<li><p>Don't be afraid of being a follower with a group of other people. Especially during the first hour where less people are in the building. But following a side character can be equally rewarding.</p>
</li>
<li><p>Following loud music might lead you to interesting places.</p>
</li>
</ul>
<h2 id="heading-additional-reading">Additional Reading</h2>
<p><strong>EDIT:</strong> Since the show has announced it's <s>final</s> <s>final</s> <em>final</em> extension, you might only able to see it once (if at all). So I strongly recommend making yourself familiar with the story.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=PDEyq48CwNw">https://www.youtube.com/watch?v=PDEyq48CwNw</a></div>
<p> </p>
<p>You might also consider reading another <a target="_blank" href="https://dquinn.net/a-spoiler-free-primer-for-sleep-no-more-in-nyc/">spoiler-free primer for Sleep No More</a> which has a bit more information.</p>
]]></content:encoded></item><item><title><![CDATA[Exhaustive Checks with Mypy]]></title><description><![CDATA[If you want to exhaustively check an enum (or a literal) and have Mypy tell you when you forgot a case, you can write yourself a tiny utility function:
from typing import NoReturn

def unreachable(v: NoReturn) -> NoReturn:
    raise AssertionError()
...]]></description><link>https://blog.mrcljx.dev/exhaustive-checks-with-mypy</link><guid isPermaLink="true">https://blog.mrcljx.dev/exhaustive-checks-with-mypy</guid><category><![CDATA[Python]]></category><category><![CDATA[Mypy]]></category><dc:creator><![CDATA[Marcel Jackwerth]]></dc:creator><pubDate>Thu, 26 Jan 2023 13:55:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Ctaj_HCqW84/upload/9aa49e6361580e35191a3b8f182eef8f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you want to exhaustively check an enum (or a literal) and have Mypy tell you when you forgot a case, you can <a target="_blank" href="https://mypy.readthedocs.io/en/latest/literal_types.html#exhaustiveness-checking">write yourself a tiny utility function</a>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> NoReturn

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">unreachable</span>(<span class="hljs-params">v: NoReturn</span>) -&gt; NoReturn:</span>
    <span class="hljs-keyword">raise</span> AssertionError()
</code></pre>
<p>You can then use it with <code>match</code> (on Python 3.10) or <code>is</code>:</p>
<pre><code class="lang-python">
<span class="hljs-keyword">import</span> enum

<span class="hljs-keyword">from</span> .utils <span class="hljs-keyword">import</span> unreachable

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Fruit</span>(<span class="hljs-params">enum.Enum</span>):</span>
    APPLE = enum.auto()
    BANANA = enum.auto()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">using_match</span>(<span class="hljs-params">fruit: Fruit</span>) -&gt; int:</span>
    match fruit:
        case Fruit.APPLE:
            <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
        case Fruit.BANANA:
            <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>
        case _:
            unreachable(fruit)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">using_is</span>(<span class="hljs-params">fruit: Fruit</span>) -&gt; int:</span>
    <span class="hljs-keyword">if</span> fruit <span class="hljs-keyword">is</span> Fruit.APPLE:
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
    <span class="hljs-keyword">elif</span> fruit <span class="hljs-keyword">is</span> Fruit.BANANA:
        <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>
    <span class="hljs-keyword">else</span>:
        unreachable(fruit)
</code></pre>
<p>But beware that <a target="_blank" href="https://github.com/python/mypy/issues/6366">this won't work for enums</a> when using <code>==</code> as Mypy can't know whether the operator has been overridden:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">using_eq</span>(<span class="hljs-params">fruit: Fruit</span>) -&gt; int:</span>
    <span class="hljs-keyword">if</span> fruit == Fruit.APPLE:
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
    <span class="hljs-keyword">elif</span> fruit == Fruit.BANANA:
        <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-comment"># error: Argument 1 to "unreachable" has incompatible</span>
        <span class="hljs-comment">#   type "Fruit"; expected "NoReturn"  [arg-type]</span>
        unreachable(fruit)
</code></pre>
]]></content:encoded></item><item><title><![CDATA[The useEvent Hook for React]]></title><description><![CDATA[Dan Abramov has opened an RFC proposing the addition of a useEvent hook to React. It's meant to allow for this change in your codebase:
- const onClick = useCallback(() => console.log("Hello", name), [name]);
+ const onClick = useEvent(() => console....]]></description><link>https://blog.mrcljx.dev/the-useevent-hook-for-react</link><guid isPermaLink="true">https://blog.mrcljx.dev/the-useevent-hook-for-react</guid><category><![CDATA[React]]></category><category><![CDATA[react hooks]]></category><category><![CDATA[useCallback]]></category><category><![CDATA[ReactHooks]]></category><dc:creator><![CDATA[Marcel Jackwerth]]></dc:creator><pubDate>Fri, 26 Aug 2022 09:49:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/KBE_Bvq1U30/upload/7614090c2de64bc1f13ecc8da07cd5ab.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Dan Abramov has <a target="_blank" href="https://github.com/reactjs/rfcs/pull/220">opened an RFC</a> proposing <a target="_blank" href="https://github.com/facebook/react/pull/24505">the addition</a> of a <code>useEvent</code> hook to React. It's meant to allow for this change in your codebase:</p>
<pre><code class="lang-diff"><span class="hljs-deletion">- const onClick = useCallback(() =&gt; console.log("Hello", name), [name]);</span>
<span class="hljs-addition">+ const onClick = useEvent(() =&gt; console.log("Hello", name));</span>
</code></pre>
<h1 id="heading-performance-optimization">Performance Optimization</h1>
<p>When you're running into performance issues, you'll eventually discover the higher-order component <code>memo</code> (which is quite similar to the pre-hooks base class <code>PureComponent</code>). By default React will always re-render the whole subtree of a component. If one of the child components is wrapped in <code>memo</code> though, that component will (by default) perform a shallow equality check of its props, and prevent React from traversing the tree further if they didn't change.</p>
<p>Since this relies on shallow equality checks, you'll start using <code>useCallback</code> a lot:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params">{ name }</span>) </span>{
  <span class="hljs-keyword">const</span> onClick = useCallback(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello"</span>, name);
  }, [name]);

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyMemoButton</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span> /&gt;</span></span>;
}

<span class="hljs-keyword">const</span> MyMemoButton = memo(...);
</code></pre>
<p>Note how you have to pass <code>name</code> as a dependency here. If you didn't, then clicking the button would render a stale value: the <strong>first</strong> name that was passed to the component.</p>
<p>This feels very crufty and annoying, especially as the dependency list grows.</p>
<h1 id="heading-habitual-usecallback">Habitual useCallback</h1>
<p>Especially when you're working on a large application with many people, the churn when you eventually have to add <code>memo</code> to one component is high. You'll have to find and change all the places where props are passed to it to use <code>useMemo</code> or <code>useCallback</code>. If contexts are involved, it's even harder. It's also dangerously easy to cause a performance regression because someone passed another prop to your component and didn't make the effort to use <code>useMemo</code>.</p>
<p>The habit of using <code>useCallback</code> and <code>useMemo</code> means less churn, less regressions, less performance surprises, and less mental overhead while writing and reviewing code. You can even make this conventional, and enforce it to some extent with the <a target="_blank" href="https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md">jsx-no-bind</a> ESLint rule in your codebase (which by default will not <code>allowArrowFunctions</code>).</p>
<h2 id="heading-good-habit-or-bad-habit">Good Habit or Bad Habit</h2>
<p>There are arguments to <a target="_blank" href="https://kentcdodds.com/blog/usememo-and-usecallback">avoid <code>useCallback</code> and <code>useMemo</code></a> and only do this optimization once your app becomes slow.</p>
<p>The core arguments are that using them</p>
<ol>
<li><p>makes code harder to write and read,</p>
</li>
<li><p>adds constant baseline cost (time and memory), and</p>
</li>
<li><p>adds variable memory cost (by unnecessarily holding references).</p>
</li>
</ol>
<p>While (1) is absolutely true, (2) should be in the realm of nanoseconds and bytes, while (3) requires a bit more context:</p>
<p>When returning large, transformed objects from <code>useMemo</code> this is true. However in that case the calculation of that object was also probably expensive. So holding onto is also more likely to be preferable. When using <code>useCallback</code> for event handlers, they will be referenced by the DOM anyway.</p>
<p>If we can fix the developer experience of (1), then I'd happily accept (2) and (3) if I get a scalable application in exchange. Furthermore, it's not just about performance but also about side-effects. We'll get to that in a second.</p>
<h1 id="heading-addressing-the-developer-experience">Addressing the Developer Experience</h1>
<p>If you have been using React for a while, you might remember that before we had hooks, this felt easier:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">PureComponent</span> </span>{
  handleClick() {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello"</span>, <span class="hljs-built_in">this</span>.props.name);
  }

  render() {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyPureButton</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{this.handleClick}</span> /&gt;</span></span>;
  }
}
</code></pre>
<p>With <code>useEvent</code> we get this experience back!</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params">{ name }</span>) </span>{
  <span class="hljs-keyword">const</span> onClick = useEvent(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello"</span>, name);
  });

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyMemoButton</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span> /&gt;</span></span>;
}
</code></pre>
<h1 id="heading-side-effects">Side-Effects</h1>
<p>Your codebase might have side-effects, often around data loading.</p>
<pre><code class="lang-js">useEffect(<span class="hljs-function">() =&gt;</span> {
  setOtherParties([]);
  api.listParties(date)
    .then(<span class="hljs-function"><span class="hljs-params">parties</span> =&gt;</span> parties.filter(<span class="hljs-function"><span class="hljs-params">b</span> =&gt;</span> b.host !== user))
    .then(setOtherParties);
}, [date, user, setOtherParties]);
</code></pre>
<p>Note how this effect triggers whenever the <code>date</code> or the <code>user</code> changes (for example when changing accounts). Extracting a predicate naively would lead to issues. Since <code>filter</code> is not stable, it would constantly cause the effect:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> filter = <span class="hljs-function">(<span class="hljs-params">party</span>) =&gt;</span> party.host !== user;

useEffect(<span class="hljs-function">() =&gt;</span> {
  setOtherParties([]);
  api.listParties(date)
    .then(<span class="hljs-function"><span class="hljs-params">parties</span> =&gt;</span> parties.filter(filter))
    .then(setOtherParties);
}, [date, filter, setOtherParties]);
</code></pre>
<p>The <code>exhaustive-deps</code> ESLint rule (which does more than its name suggests) would call out that issue and encourage you to fix this by wrapping it in <code>useCallback</code>, like this:</p>
<pre><code class="lang-python">const filter = useCallback((party) =&gt; party.host !== name, [name]);
</code></pre>
<p>If the name changes, the reference will change, and will cause the effect when intended. However, the lint rule <strong>can not check this across component boundaries</strong>!</p>
<h2 id="heading-predicates-setters-andamp-loaders">Predicates, Setters &amp; Loaders</h2>
<p>In the situation above, <code>useEvent</code> would probably be wrong and a bug.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> filter = useEvent(<span class="hljs-function">(<span class="hljs-params">party</span>) =&gt;</span> party.host !== name);
</code></pre>
<p>If you were to do the following, a change of the name would not cause the effect.</p>
<h2 id="heading-effects-andamp-events">Effects &amp; Events</h2>
<p>If we look at a different example, something is wrong:</p>
<pre><code class="lang-python">const greet = useCallback((name) =&gt; {
  console.log(lang == <span class="hljs-string">"es"</span> ? <span class="hljs-string">"Hola"</span> : <span class="hljs-string">"Hello"</span>, name);
}, [lang]);

useEffect(() =&gt; {
  greet(user.name);
}, [user]);
</code></pre>
<p>When the <code>user</code> changes, then a new greeting is emitted. But when the locale changes, the user is greeted again.</p>
<pre><code class="lang-python">const greet = useCallback((name) =&gt; {
  console.log(lang == <span class="hljs-string">"es"</span> ? <span class="hljs-string">"Hola"</span> : <span class="hljs-string">"Hello"</span>, name);
}, [lang]);

useEffect(() =&gt; {
  greet(user.name);
}, [user]);
</code></pre>
<p>In this case, <code>useEvent</code> will give us the desired behavior.</p>
<h1 id="heading-but-isnt-this-exactly-like">But isn't this exactly like…</h1>
<p>It might seem that <code>useEvent</code> might just be the same as some other hooks that we already have, but it's not.</p>
<pre><code class="lang-python">// stable, but stale name on second invocation
const greet = useRef((name) =&gt; console.log(name)).current;

// stable, but stale name on second invocation
const greet = useCallback((name) =&gt; console.log(name), []);

// <span class="hljs-keyword">not</span> stable
// useLatest coming <span class="hljs-keyword">from</span> a library like react-use
const greet = useLatest((name) =&gt; console.log(name));
</code></pre>
<h1 id="heading-closing-notes">Closing Notes</h1>
<p>So when do I use which? My rough guideline is that if a callback is named <code>on...</code>, <code>handle...</code>, <code>dispatch...</code>, <code>set...</code> and has no return value, it's a good candidate for <code>useEvent</code>. Otherwise there's a good chance it's more of a predicate or loader, in which case <code>useCallback</code> is the preferred choice still.</p>
<p>It would be great if the ESLint <code>exhaustive-deps</code> ESLint rule could treat return values of <code>useEvent</code> like <code>useRef</code> and know that they are stable, so that one doesn't need to include them in dependencies further down. I had to fork the ESLint plugin to make it do so.</p>
<p>As I went through our codebase, I found that I was able to change more than 90% of our <code>useCallback</code> to <code>useEvent</code>, reducing quite a bit of cruft. A great change!</p>
]]></content:encoded></item><item><title><![CDATA[About React Keys]]></title><description><![CDATA[This is mostly a rephrased version of React's official documentation on Lists and Keys with some additional examples.
React allows specifying a key property on a component. It lets React identify components and track their identities.
Iterating over ...]]></description><link>https://blog.mrcljx.dev/about-react-keys</link><guid isPermaLink="true">https://blog.mrcljx.dev/about-react-keys</guid><category><![CDATA[React]]></category><dc:creator><![CDATA[Marcel Jackwerth]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:09:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/r8VbpgMS6Uc/upload/78fd76c7807ef4042aa1f6f7a51f0fea.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is mostly a rephrased version of React's official documentation on <a target="_blank" href="https://reactjs.org/docs/lists-and-keys.html#keys">Lists and Keys</a> with some additional examples.</p>
<p>React allows specifying a <code>key</code> property on a component. It lets React identify components and track their identities.</p>
<h2 id="heading-iterating-over-arrays">Iterating over Arrays</h2>
<p>Imagine we have a stateful component <code>EditableFruit</code> that also has side-effects.</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">EditableFruit</span>(<span class="hljs-params">{ value: initialValue }</span>) </span>{
  <span class="hljs-keyword">const</span> [value, setValue] = useState(initialValue);

  useEffect(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"init"</span>, initialValue)); <span class="hljs-comment">// "once" effect</span>

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>{initialValue}<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{value}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setValue(e.currentTarget.value)} /&gt;
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
}
</code></pre>
<p>And a container that uses this component.</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Example</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> fruits = [<span class="hljs-string">"APPLE"</span>, <span class="hljs-string">"BANANA"</span>];

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    {fruits.map((fruit, index) =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">EditableFruit</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{SEE_BELOW}</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{fruit}</span> /&gt;</span>)}
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
}
</code></pre>
<p>We have a few options what to use for <code>key</code>.</p>
<h3 id="heading-using-a-unique-key">Using a unique key</h3>
<p>If each element has some key that is guaranteed to be unique (in this case <code>fruit</code> itself), always use that. When the order of the array changes, React will be able to re-associate the DOM elements and React components with the array items.</p>
<p>Adding an <code>AVOCADO</code> in the middle will log <code>init AVOCADO</code>. Everything "works" as one might expect.</p>
<h3 id="heading-using-the-index">Using the index</h3>
<p>If no unique key exists, using the <code>index</code> might be the only option.</p>
<blockquote>
<p>When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort.</p>
</blockquote>
<p>However it means React won't be able to semantically identify elements which can lead to bugs, especially when the components contain state or effects.</p>
<p>Adding an <code>AVOCADO</code> in the middle will log <code>init BANANA</code>. The rendered inputs will contain the values <code>APPLE</code>, <code>BANANA</code> <strong>(!)</strong>, and <code>BANANA</code>.</p>
<p>Because of this, the React documentation states that using <code>index</code> should be a last resort.</p>
<p>The ESLint rule <a target="_blank" href="https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md"><code>react/no-array-index-key</code></a> can detect this, forcing you to add an ignore if one really want to accept this risk. In the current codebase I'm working on (which is using TypeScript) we can use <code>key={index!}</code> to silence the linter.</p>
<h3 id="heading-using-the-index-joined-with-a-potentially-unique-key">Using the index joined with a (potentially) unique key</h3>
<p>Using a key like <code>${fruit}-${index}</code> will avoid the issue where state might be associated with the wrong component when just using <code>index</code>. However, this is a heavy shotgun comes at the cost of performance.</p>
<p>Adding an <code>AVOCADO</code> in the middle will log <code>init AVOCADO</code> and <code>init BANANA</code>. This makes sense when you look at what React has to do to determine the changes:</p>
<pre><code class="lang-python">diff({
  <span class="hljs-keyword">from</span>: [<span class="hljs-string">"APPLE-0"</span>, <span class="hljs-string">"BANANA-1"</span>],
  to: [<span class="hljs-string">"APPLE-0"</span>, <span class="hljs-string">"AVOCADO-1"</span>, <span class="hljs-string">"BANANA-2"</span>]
}) // { added: [<span class="hljs-string">"AVOCADO-1"</span>, <span class="hljs-string">"BANANA-2"</span>], removed: [<span class="hljs-string">"BANANA-1"</span>] }
</code></pre>
<p>There are situations where this might be acceptable, but I would be careful when performance is critical, especially when the array is large.</p>
<h3 id="heading-not-providing-a-key">Not providing a key</h3>
<p>Not providing a <code>key</code> is effectively the same as passing <code>index</code>.</p>
<p>The ESLint rule <a target="_blank" href="https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md"><code>react/jsx-key</code></a> can detect this forcing you to make a conscious choice.</p>
<h2 id="heading-resetting-a-components-state">Resetting a component's state</h2>
<p>I sometimes find myself using <code>key</code> as a quick (and dirty) way to reset the state of a component, where I really want a big hammer.</p>
<pre><code class="lang-python">function Example() {
  const [nonce, setNonce] = useState(Date.now());

  <span class="hljs-keyword">return</span> &lt;div&gt;
    &lt;Button onClick={() =&gt; setNonce(Date.now())} /&gt;
    &lt;Universe key={nonce} /&gt;
  &lt;/div&gt;
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Hello World]]></title><description><![CDATA[201 Created]]></description><link>https://blog.mrcljx.dev/hello-world</link><guid isPermaLink="true">https://blog.mrcljx.dev/hello-world</guid><category><![CDATA[Hello World]]></category><dc:creator><![CDATA[Marcel Jackwerth]]></dc:creator><pubDate>Thu, 05 May 2022 13:51:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/vXInUOv1n84/upload/7bed80e500df2349cd27bcf18aa64022.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>201 Created</p>
]]></content:encoded></item></channel></rss>