DEV Community

Daniel Macák
Daniel Macák

Posted on • Edited on

SolidJS: Purpose of the `keyed` attribute in <Show>

If you are like me, you are thoroughly confused by the keyed attribute in the <Show> control flow component from Solid's core library. Well not anymore since I'll explain everything. Let's talk why you should care and how to use it properly.

I first met with keyed when I wanted to use <Show> in a more convenient way. So typically one uses Show like this:

<Show when={currentUser()}>
  {children}
</Show>
Enter fullscreen mode Exit fullscreen mode

And indeed if that's all you need, you don't have to care. The problem is that, if you are using any static type checking like TypeScript, the currentUser() part can at least in theory return falsy value. So if you plan to use it inside <Show>, you'd have to check for truthiness all the time.

<Show when={currentUser()}>
  <>
    <span>{currentUser()?.name}</span>
    <span>{currentUser()?.age}</span>
  </>
</Show>
Enter fullscreen mode Exit fullscreen mode

Which is inefficient and tedious, but even worse it's logically unnecessary because you already know inside the <Show> that the currentUser() exists, otherwise it wouldn't get displayed. But since the type system doesn't have the guarantee that each call to currentUser() returns the same value, ie. it can be impure, it has to assume that subsequent calls to currentUser() can still return falsy value. And calling it inside <Show> JSX tag doesn't change that fact in any way, because <span>{currentUser()?.name}</span> is not scoped to <Show>, but rather to the whole component.

Enter the keyed attribute

There is actually great solution and that's using so called type narrowing render function, which looks like this:

<Show when={currentUser()}>
  {user => <>
    <span>{user().name}</span>
    <span>{user().age}</span>
  </>}
</Show>
Enter fullscreen mode Exit fullscreen mode

It narrows the type of user to truthy value since it already knows it's truthy by showing Show's children. Perfect!

But wait a second, how's this connected to the keyed attribute? Well before Solid 1.7.0, it wasn't possible to use this render function without using keyed as well.

<Show when={currentUser()} keyed>
  {user => <>
    <span>{user.name}</span>
    ...
Enter fullscreen mode Exit fullscreen mode

This, and the fact the docs don't explain keyed led to developers thinking this attribute is just about being able to use the render function, but that's wrong! Starting with Solid 1.7.0 the render function can be used also without keyed, but what's its purpose then?

Purpose of keyed

If you have experience with React, keyed is somewhat similar to React key. React uses it to keep track of DOM elements so that it can efficiently reuse them instead of destroying and recreating.

And so does Solid. If you don't use keyed, when you change currentUser to someone else, Solid expects the Show's content not to have radically changed and it reuses the already existing component references and their respective DOM nodes instead of destroying and recreating them, but it of course updates the content based on the changed reactive properties.

However if you use keyed and the value in when attribute has changed, Solid will actually recreate the Show's children from scratch. You can see it in this example:

Every time you click 'Change current user', currentUser's reference changes, which causes Show's children to be recreated and <UserDetails> prints 'mounted' to the console. But if you remove keyed from Show (don't forget to also rewrite user() to just user inside Show), it doesn't get mounted more than once.

Usage of keyed

I'd say you don't want to use keyed in most cases. First of all you might lose the benefit of type narrowing:

<Show when={currentUser()?.id} keyed>
  {id => <>
    <span>{currentUser()!.name}</span>
    ...
Enter fullscreen mode Exit fullscreen mode

Notice that when now uses the id, not just the object reference. That's because in practice the currentUser() reference can change even though the user properties used inside <Show> can stay the same. As a result, the render function now accepts just the id parameter and not the whole user, which is not necessarily very helpful.

Secondly it can lead to unnecessary re-renders of <Show>'s content, typically when you use object references in when attribute. This can lead to undesired side effects. If your component performs initialization like fetching and it gets created from scratch every time the Show's when attribute changes, it can get pretty inefficient.

So much for dissuading you from using it. However, keyed can be still very helpful. If <Show>'s content needs to be completely recreated, keyed is a neat way to do so:

<Show when={currentUser()}>
  {user => 
    <Show when={user().id} keyed>
      <StatefulComponentXY user={user()} />
      ...
Enter fullscreen mode Exit fullscreen mode

If it's not simple to accommodate StatefulComponentXY to a new user due to its state or the fact that the component shouldn't be visible with the previous user shown (the old user might be cached in a resource, waiting for refetch in the meantime), keyed is a simple way to ensure that. For this purpose, I use this perhaps strange pattern with <Show keyed> only checking the identity, not reference, of the principal object and when it changes, it recreates its content entirely.

Top comments (0)