S
SolidJS7mo ago
Hussein

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
Hussein
HusseinOP7mo ago
chat() is returning undefined because its not waiting for the promise to resolve.
edygar
edygar7mo 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,
},
});
}
})
Hussein
HusseinOP7mo 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
edygar7mo 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>
Hussein
HusseinOP7mo 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
edygar7mo ago
I was trying to reproduce on the playground
Hussein
HusseinOP7mo ago
ah. ok. take your time!
edygar
edygar7mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
edygar
edygar7mo ago
The script tag is only added when the suspense is lift Oh, forgot to make it "random" xD wait a bit
edygar
edygar7mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
edygar
edygar7mo ago
Ok, on this example, only when Date.now() returns a even nubmer it loads the catpcha
Hussein
HusseinOP7mo 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
edygar7mo ago
aahá!
Hussein
HusseinOP7mo ago
shouldShowChat should indicate that the captcha was done correctly, but this logic is not implemented yet.
edygar
edygar7mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
edygar
edygar7mo ago
I put an alert to help us there xD
Hussein
HusseinOP7mo ago
🤣
edygar
edygar7mo 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
Hussein
HusseinOP7mo ago
i already tried <Show>, it didn't work
edygar
edygar7mo ago
It does work the way I did there. fallback={Recaptcha} That's the trick, it only calls Recaptcha after the suspense is lift
Hussein
HusseinOP7mo 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
edygar7mo ago
😭 f*ck, I don't get why createAsync signal doesnt return the same flags as createResource does, it would be way simpler
Hussein
HusseinOP7mo ago
yeah idk
peerreynders
peerreynders7mo 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
Hussein
HusseinOP7mo ago
didn't do what i wanted
Brendonovich
Brendonovich7mo ago
tbf you don't have to use createAsync, createResource is still perfectly useful
Hussein
HusseinOP7mo ago
will it fix my issue?
edygar
edygar7mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Hussein
HusseinOP7mo ago
nope i'm starting to think this isn't possible haha
edygar
edygar7mo ago
🤣 don't think so, It's just my inexperience
Brendonovich
Brendonovich7mo 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
Hussein
HusseinOP7mo ago
finally. it did! thank you lol
edygar
edygar7mo 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>
Hussein
HusseinOP7mo 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
peerreynders7mo ago
createAsync solution.
peerreynders
peerreynders7mo 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().
Hussein
HusseinOP5mo 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
Want results from more Discord servers?
Add your server