How do i conditionally add something to <head> based on what createAsync returns?

I need to wait for a resource coming from createAsync so then i can decide whether i should add google recaptcha to <head> using useHead from @solidjs/meta
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

if (!chat())
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

if (!chat())
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
38 Replies
exercise
exerciseOP2y ago
chat() is returning undefined because its not waiting for the promise to resolve.
edygar
edygar2y ago
It seems you're intending to create an effect to when chat is done loading:
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

createEffect(() => {
if (chat()) {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
}
})
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

createEffect(() => {
if (chat()) {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
}
})
exercise
exerciseOP2y ago
would that work with ssr? i'm using solid start i need the <script> to be injected in ssr i think createEffect is for client only?
edygar
edygar2y ago
oh, got it. Sorry. And I was looking, it seems solid meta doesnt support <Script> right? I think you could create your own and put it into a Suspense boundary:
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
}

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

return <Suspense>
{chat() && <Recaptcha />}
</Suspense>
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
}

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

return <Suspense>
{chat() && <Recaptcha />}
</Suspense>
exercise
exerciseOP2y ago
yeah solid meta doesn't support <Script> but useHead adds the ability to inject any tag. nice! i will see oops. that didn't work. its injecting the <script> in all cases. @edygar do you have any other fixes?
edygar
edygar2y ago
I was trying to reproduce on the playground
exercise
exerciseOP2y ago
ah. ok. take your time!
edygar
edygar2y ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
edygar
edygar2y ago
The script tag is only added when the suspense is lift Oh, forgot to make it "random" xD wait a bit
edygar
edygar2y ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
edygar
edygar2y ago
Ok, on this example, only when Date.now() returns a even nubmer it loads the catpcha
exercise
exerciseOP2y ago
this changes nothing in the example u already gave me here which didn't work sadly maybe i should give you the whole code i have?
import { Meta, Title, useHead } from "@solidjs/meta";
import { For, Suspense } from "solid-js";
import { cache, createAsync } from "@solidjs/router";
import "../assets/contact.css";

const shouldShowChat = true;

const getChat = cache(async () => {
"use server";
return shouldShowChat
? {
messages: [{ text: "test", author: "me" }],
}
: null;
}, "chat");

export const route = { load: getChat };

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});

return (
<div
class="g-recaptcha"
data-sitekey="6LdyF4wpAAAAAIAF7YO-78xmEsupJvXjZ9drwCsF"
/>
);
};

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

return (
<main>
<h1>Contact</h1>
<Suspense>
done{" "}
{chat() ? (
<For each={chat()?.messages}>
{(message) => (
<p>
{message.author}: {message.text}
</p>
)}
</For>
) : (
<>
<p>
For security purposes, we want you to complete this challenge:
</p>
<form>
<Recaptcha />
<button>Create new chat</button>
</form>
</>
)}
</Suspense>
</main>
);
}
import { Meta, Title, useHead } from "@solidjs/meta";
import { For, Suspense } from "solid-js";
import { cache, createAsync } from "@solidjs/router";
import "../assets/contact.css";

const shouldShowChat = true;

const getChat = cache(async () => {
"use server";
return shouldShowChat
? {
messages: [{ text: "test", author: "me" }],
}
: null;
}, "chat");

export const route = { load: getChat };

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});

return (
<div
class="g-recaptcha"
data-sitekey="6LdyF4wpAAAAAIAF7YO-78xmEsupJvXjZ9drwCsF"
/>
);
};

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

return (
<main>
<h1>Contact</h1>
<Suspense>
done{" "}
{chat() ? (
<For each={chat()?.messages}>
{(message) => (
<p>
{message.author}: {message.text}
</p>
)}
</For>
) : (
<>
<p>
For security purposes, we want you to complete this challenge:
</p>
<form>
<Recaptcha />
<button>Create new chat</button>
</form>
</>
)}
</Suspense>
</main>
);
}
if shouldShowChat is true, captcha script shouldn't be in <head>
edygar
edygar2y ago
aahá!
exercise
exerciseOP2y ago
shouldShowChat should indicate that the captcha was done correctly, but this logic is not implemented yet.
edygar
edygar2y ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
edygar
edygar2y ago
I put an alert to help us there xD
exercise
exerciseOP2y ago
🤣
edygar
edygar2y ago
Recaptcha was being called while we were loading, even with the suspense. Now it returns a function so it will only be called when the "element" was supposed to be included
exercise
exerciseOP2y ago
i already tried <Show>, it didn't work
edygar
edygar2y ago
It does work the way I did there. fallback={Recaptcha} That's the trick, it only calls Recaptcha after the suspense is lift
exercise
exerciseOP2y ago
yeah i did that exactly maybe it works for u in solid CSR but it didn't for me in solid start with ssr and also it gives a ts error because the fallback needs to be <Recaptcha/> and Recaptcha should return <></>
edygar
edygar2y ago
😭 f*ck, I don't get why createAsync signal doesnt return the same flags as createResource does, it would be way simpler
exercise
exerciseOP2y ago
yeah idk
peerreynders
peerreynders2y ago
Still catching up on the thread but to delay the SSR response until an async op has settled use { deferStream: true} on createAsync.
GitHub
solid-router/src/data/createAsync.ts at a2652b4eab6576db78e6371e5c0...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
exercise
exerciseOP2y ago
didn't do what i wanted
Brendonovich
Brendonovich2y ago
tbf you don't have to use createAsync, createResource is still perfectly useful
exercise
exerciseOP2y ago
will it fix my issue?
edygar
edygar2y ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
exercise
exerciseOP2y ago
nope i'm starting to think this isn't possible haha
edygar
edygar2y ago
🤣 don't think so, It's just my inexperience
Brendonovich
Brendonovich2y ago
does this work?
import { Meta, Title, useHead } from "@solidjs/meta";
import { For, Match, Suspense, Switch } from "solid-js";
import { cache, createAsync } from "@solidjs/router";
import "../assets/contact.css";

const shouldShowChat = true;

const getChat = cache(async () => {
"use server";
return shouldShowChat ? { messages: [{ text: "test", author: "me" }] } : null;
}, "chat");

export const route = { load: getChat };

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true
}
});

return <div class="g-recaptcha" data-sitekey="6LdyF4wpAAAAAIAF7YO-78xmEsupJvXjZ9drwCsF" />;
};

export default function Contact() {
const chat = createAsync(() => getChat());

return (
<main>
<h1>Contact</h1>
<Suspense>
done{" "}
<Switch>
<Match when={chat()}>
{chat => (
<For each={chat()?.messages}>
{message => (
<p>
{message.author}: {message.text}
</p>
)}
</For>
)}
</Match>
<Match when={chat() === null}>
<>
<p>For security purposes, we want you to complete this challenge:</p>
<form>
<Recaptcha />
<button>Create new chat</button>
</form>
</>
</Match>
</Switch>
</Suspense>
</main>
);
}
import { Meta, Title, useHead } from "@solidjs/meta";
import { For, Match, Suspense, Switch } from "solid-js";
import { cache, createAsync } from "@solidjs/router";
import "../assets/contact.css";

const shouldShowChat = true;

const getChat = cache(async () => {
"use server";
return shouldShowChat ? { messages: [{ text: "test", author: "me" }] } : null;
}, "chat");

export const route = { load: getChat };

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true
}
});

return <div class="g-recaptcha" data-sitekey="6LdyF4wpAAAAAIAF7YO-78xmEsupJvXjZ9drwCsF" />;
};

export default function Contact() {
const chat = createAsync(() => getChat());

return (
<main>
<h1>Contact</h1>
<Suspense>
done{" "}
<Switch>
<Match when={chat()}>
{chat => (
<For each={chat()?.messages}>
{message => (
<p>
{message.author}: {message.text}
</p>
)}
</For>
)}
</Match>
<Match when={chat() === null}>
<>
<p>For security purposes, we want you to complete this challenge:</p>
<form>
<Recaptcha />
<button>Create new chat</button>
</form>
</>
</Match>
</Switch>
</Suspense>
</main>
);
}
this will render the captcha while getChat is loading, since during that time chat() will return undefined which is falsey, and even though there's a suspense, suspense is non-blocking and renders its children while it is suspending
exercise
exerciseOP2y ago
finally. it did! thank you lol
edygar
edygar2y ago
ahá! so the trick was === null Could you try this one? Just for learning
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
}

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

return <Suspense>
{chat()===null && <Recaptcha />}
</Suspense>
const getChat = cache(async () => {
return { messages: [{ text: "hi", author: "me" }] };
}, "chat");

const Recaptcha = () => {
useHead({
tag: "script",
id: "recaptcha",
setting: { close: true },
props: {
src: "https://www.google.com/recaptcha/api.js",
async: true,
defer: true,
},
});
}

export default function Contact() {
const chat = createAsync(() => {
return getChat();
});

return <Suspense>
{chat()===null && <Recaptcha />}
</Suspense>
exercise
exerciseOP2y ago
Recaptcha doesn't even return jsx and yeah it works this also fixed a weird bug where my captcha doesn't render some times 😆 crazy
peerreynders
peerreynders2y ago
createAsync solution.
peerreynders
peerreynders2y ago
This was in response to "didn't do what i wanted".
With if (!chat()) your code wasn't differentiating between the unresolved and resolved state of chat().
exercise
exerciseOP17mo ago
is there a way to show a suspense fallback with this solution? actually the suspense fallback is working, but not when i revalidate... i want the suspense fallback to rerender on revalidate suspense fallback only retriggers when using createResource and refetch and removing cache

Did you find this page helpful?