solid-start icon indicating copy to clipboard operation
solid-start copied to clipboard

<Title /> does not support reactive values?

Open vanillacode314 opened this issue 2 years ago • 14 comments

  <Title>
          {appState.title
            ? `${appState.title} - RedditLattice`
            : `RedditLattice`}
   </Title>

This in my root.tsx doesn't work and I have confirmed with a console log in a createEffect that appState.title does trigger a signal to change.

Relevant Source Code: https://github.com/vanillacode314/redditlattice/blob/solidjs/src/root.tsx

vanillacode314 avatar Oct 13 '22 08:10 vanillacode314

Hey @vanillacode314! In Solid, you are unable to use ternary operators to express conditional rendering blocks. You need to use the Show component in Solid to do this. The reason ternary operators don't work like they do in React is because Solid uses a fine-grained, component-based reactivity system. Because of this, components in Solid only render ONCE, and only createEffect (and other special primitives) run after the component is mounted. These special components (like Show and For) cause conditional render changes after the initial render/mount of the component by using the Solid compiler that communicates with the real DOM directly.

You can use it like so:

import { Show } from "solid-js";

<Title>
  <Show when={appState.title} fallback={`RedditLattice`}>
    `${appState.title} - RedditLattice`
  </Show>
</Title>

The when attribute will render the fallback attribute content if a falsy value is returned.

ghost avatar Oct 14 '22 04:10 ghost

From the solidjs tutorial

Solid's compiler is smart enough to optimally handle ternaries (a ? b : c) and boolean expressions (a && b).

Plus replacing it with <Show> still doesn't work

vanillacode314 avatar Oct 14 '22 06:10 vanillacode314

From the solidjs tutorial

Solid's compiler is smart enough to optimally handle ternaries (a ? b : c) and boolean expressions (a && b).

Plus replacing it with <Show> still doesn't work

Using a ternary definitely wouldn't work. Ternaries are in the form of a Javascript expression. There is absolutely zero way that Solid's compiler can handle that when the component only runs through the JSX and converts them into real DOM nodes once.

What happens when you try to use the Show component?

ghost avatar Oct 14 '22 06:10 ghost

What happens when you try to use the Show component?

It only shows what the initial state of appState.title would show, this is the exact behaviour as ternary

vanillacode314 avatar Oct 14 '22 06:10 vanillacode314

@vanillacode314 - Scratch that. I think the culprit is that you are setting the Title in the root. Because Solid Start is an SPA by default, the root.tsx is used throughout your entire application, and no page reloads occur on route navigation. Try moving the Title into each route file instead.

e.g.

routes/r/[subreddit].tsx

<Title>{appState.title}</Title> // you know that a title will be set based on the subreddit so this should be fine.

then you can set the fallback to apply to all other routes in root.tsx if you want.

<Title>RedditLattice</Title>

ghost avatar Oct 14 '22 06:10 ghost

That's what I tried but I believe this is another bug with solid-start right now that children <Title> do not override root.tsx <Title>

vanillacode314 avatar Oct 14 '22 06:10 vanillacode314

Ah, in that case, I think you will need to wait for a fix. I don't believe there is another way to do it. I haven't really played around with Solid Start too much recently so I didn't even know this was a bug. Have you filed an issue/has an issue already been filed for this?

ghost avatar Oct 14 '22 06:10 ghost

Found another weird thing

<Show when={appState.title} keyed>
  {(title) => <Title>{title} - RedditLattice</Title>}
</Show>

This works but this only shows fallback

<Show when={appState.title} keyed fallback={<div>Loading</div>}>
  {(title) => <Title>{title} - RedditLattice</Title>}
</Show>

vanillacode314 avatar Oct 14 '22 06:10 vanillacode314

Found another weird thing

<Show when={appState.title} keyed>
  {(title) => <Title>{title} - RedditLattice</Title>}
</Show>

This works but this only shows fallback

<Show when={appState.title} keyed fallback={<div>Loading</div>}>
  {(title) => <Title>{title} - RedditLattice</Title>}
</Show>

Oh, try making the when attribute a strictly boolean expression. Try appState.title !== "" or something like that (that is what I believe the default is in your application). I also forgot about the new keyed attribute on Show and For.

ghost avatar Oct 14 '22 06:10 ghost

yeah already tried that doesn't work

vanillacode314 avatar Oct 14 '22 07:10 vanillacode314

Yeah this is probably a bug, the code you wrote makes sense and should work, will explore the cause and get back

nksaraf avatar Oct 16 '22 00:10 nksaraf

Hi, not 100% if this bug relates to the following, however encountered something similar

The <Title /> component playing well with SSR, but when client side navigation takes over, the Title tag gets flushed and renders empty.

Looked into the solid-meta repo but I feel like that part is playing out well, almost feel like the solid-router or something on the solid-start client side of things not playing well with the MetaProvider.

Lil examples of the bug

Hope it can be of help

gcavanunez avatar Oct 21 '22 00:10 gcavanunez

Edit: after looking into this a bit closer, it seems like I had a bug in my own code, and the first behavior I described doesn't really happen. It's working fine. The other bug (empty tag) does still happen under some circumstances.


Hi, ~I've also run into this bug, <Title /> just doesn't behave as other head tags would (e.g. Meta, where the lowest tag "wins" and replaces any previous ones), which is unexpected~, plus I've also run into empty <title> on client-side navigation as @gcavanunez shared.

Also, I'm fairly new to Solid so I might be confused, but my understanding is that anything inside of a JSX expression is reactive, as JSX expressions get essentially compiled to "smart" createEffect-like calls, so a ternary (or absolutely any JavaScript expression that correctly uses signal or store accessors) should work, without the need for Show, although it's also my understanding that Show has some advantages that I don't remember right now over certain plain JS conditional expressions, iirc.

In conclusion, there's definitely something broken with Title in need of a bug fix, I've confirmed this in my blog :P

DaniGuardiola avatar Oct 27 '22 12:10 DaniGuardiola

Yeah you are probably right about the bug.. and you are right about your assumption about the JSX. Ternaries are reactive. And Show basically is slightly more optimal where it will avoid recreating dom elements if it doesn thave to.

nksaraf avatar Oct 27 '22 21:10 nksaraf

fixed upstream in @solidjs/meta

vanillacode314 avatar Nov 12 '22 12:11 vanillacode314