Astro/Solidjs

Greetings! How could I encapsulate this logic so I could use it in multiple components? It would be similar to using React custom hooks. I tried to do it in a normal FN, but it didn't work as expected.
No description
20 Replies
edard3v 🦀
edard3v 🦀OP7d ago
I tried doing this, but it renders the html first instead of null (which is what I want)
No description
No description
peerreynders
peerreynders6d ago
useProtected() returns: - null on failure - undefined on success … but you're not paying attention to that return value. So it goes ahead and renders.
edard3v 🦀
edard3v 🦀OP6d ago
I can't see it
peerreynders
peerreynders6d ago
export function useProtected() {
const store = useStore(loginStore.store);
const isAdmin = () =>
store.token && loginStore.getPayload()?.role === Role.admin;

createEffect(() => {
if (!isAdmin()) {
navigate(LOGIN.href);
}
});

if (isAdmin()) return true;

navigate(LOGIN.href);
return false;
}
export function useProtected() {
const store = useStore(loginStore.store);
const isAdmin = () =>
store.token && loginStore.getPayload()?.role === Role.admin;

createEffect(() => {
if (!isAdmin()) {
navigate(LOGIN.href);
}
});

if (isAdmin()) return true;

navigate(LOGIN.href);
return false;
}
export default function BillsIsland() {
const isAdmin = useProtected();

return (
<Show when={isAdmin}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
export default function BillsIsland() {
const isAdmin = useProtected();

return (
<Show when={isAdmin}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
edard3v 🦀
edard3v 🦀OP6d ago
mmm ok ok. Now I understand, I imagine Show renders null meanwhile. thanks
edard3v 🦀
edard3v 🦀OP6d ago
The guy who invented this way of writing instead of separating with _ or - deserves to die xD How difficult it is to read some words.
No description
peerreynders
peerreynders6d ago
When I see snake case my brain expects Elixir code. Camelcase puts it into “look for weird JS stuff” mode. Though I've had years to acclimatize to camelCase with C# and Java. So it's a “when in Rome …” thing rather than preference.
I imagine Show renders null meanwhile
The original issue was that in refactoring you removed the decision (in BillsIsland) to render null entirely. useProtected may have returned null but that did not influence what the new BillsIsland rendered. You could have done this (assuming useProtected returned a boolean):
export default function BillsIsland() {
if (!useProtected()) return null;

return (
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
);
}
export default function BillsIsland() {
if (!useProtected()) return null;

return (
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
);
}
But that isn't idiomatic Solid. In this particular case it will work because the value returned from useProtected isn't reactive, so it won't change in the future needing to render “the other thing”. Early returns are normal in React component functions because they are fundamentally render functions. Early returns do not work with Solid because component functions create the DOM wrapped inside a render effect exactly once; so there should only be one, single blob of JSX being returned from a Solid component function (referencing all the reactive dependencies it needs to function over its lifetime). That's how we end up with:
export default function BillsIsland() {
const isAdmin = useProtected();

return (
<Show when={isAdmin}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
export default function BillsIsland() {
const isAdmin = useProtected();

return (
<Show when={isAdmin}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
This version would work if isAdmin was reactive—so if this island were to persist over navigations that Show would kick into action and render its children as soon a isAdmin() swaps from false to true.
peerreynders
peerreynders6d ago
Hint: If this island does persist across navigations then useProtected needs to return a reactive value!
export function useProtected() {
const store = useStore(loginStore.store);
// Derived Signal
// https://docs.solidjs.com/concepts/derived-values/derived-signals
const isAdmin = () =>
store.token && loginStore.getPayload()?.role === Role.admin;

// This effect will navigate anyway the first time it runs
createEffect(() => {
if (!isAdmin()) {
navigate(LOGIN.href);
}
});

return isAdmin;
}
export function useProtected() {
const store = useStore(loginStore.store);
// Derived Signal
// https://docs.solidjs.com/concepts/derived-values/derived-signals
const isAdmin = () =>
store.token && loginStore.getPayload()?.role === Role.admin;

// This effect will navigate anyway the first time it runs
createEffect(() => {
if (!isAdmin()) {
navigate(LOGIN.href);
}
});

return isAdmin;
}
export default function BillsIsland() {
const isAdmin = useProtected();
// wait until `isAdmin()` switches to `true` to render `children`
return (
<Show when={isAdmin()}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
export default function BillsIsland() {
const isAdmin = useProtected();
// wait until `isAdmin()` switches to `true` to render `children`
return (
<Show when={isAdmin()}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
Now the disappointing thing is that apparently navigate isn't enough to stop the render from happening. But if I understand correctly, Astro's SPA mode seems to be a modern version of pjax/Turbolinks (circa 2010); so it would need to request the page from the server first—and only once the response was received can it slice out the (static) bits it needs to slap it into the regions of the currently displayed page (with view transitions of course). So navigate being asynchronous wouldn't stop the synchronous render from happening.
Chris Wanstrath
The GitHub Blog
The Tree Slider
Those of you running recent versions of Safari, Chrome, or Firefox 4 may have noticed some changes to tree browsing on GitHub. The new HTML5 History API (which really has…
edard3v 🦀
edard3v 🦀OP6d ago
Yes, I was also expecting Navigate to kick me out of the component. Since I studied Rust syntax a little, I fell in love with x_y_z. It seemed strange to me at first, but then it became more comfortable to use and read. I'm wondering how I could call hooks in Solid. UseProtected seems strange to me. I see it more as a slightly special utility, but I don't know what they call it in Solid. xD I've been trying both methods, but they're not working. I'll get some sleep and try again later.
edard3v 🦀
edard3v 🦀OP6d ago
ok I already slept and now it works. Who knows what the hell I was doing before because I feel like I did the same thing xD @peerreynders
No description
peerreynders
peerreynders6d ago
This version might work as well:
function useProtected() {
const store = useStore(loginStore.store);
// Derived Signal
// https://docs.solidjs.com/concepts/derived-values/derived-signals
return () =>
store.token && loginStore.getPayload()?.role === Role.admin;
}

function BillsIsland() {
const isAdmin = useProtected();
// wait until `isAdmin()` switches to `true` to render `children`
return (
<Show when={isAdmin()}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
function useProtected() {
const store = useStore(loginStore.store);
// Derived Signal
// https://docs.solidjs.com/concepts/derived-values/derived-signals
return () =>
store.token && loginStore.getPayload()?.role === Role.admin;
}

function BillsIsland() {
const isAdmin = useProtected();
// wait until `isAdmin()` switches to `true` to render `children`
return (
<Show when={isAdmin()}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
Because JSX is implicitly wrapped in an effect the separate effect isn't needed.
edard3v 🦀
edard3v 🦀OP6d ago
Yes, I see that since Signal is reactive, it changes the Show re-render again. Right?
edard3v 🦀
edard3v 🦀OP6d ago
but the detail is that I couldn't do the navigate(). I think xD
No description
edard3v 🦀
edard3v 🦀OP6d ago
It works, but it doesn't kick me out xD
edard3v 🦀
edard3v 🦀OP6d ago
No description
peerreynders
peerreynders6d ago
You need your own version of this:
peerreynders
peerreynders6d ago
https://github.com/solidjs/solid-router/blob/3c214ce2ceb9b7d9d39d143229a8c6145e83e681/src/components.tsx#L80-L87
function BillsIsland() {
const isAdmin = useProtected();
// wait until `isAdmin()` switches to `true` to render `children`
return (
<Show when={isAdmin()} fallback={<Navigate href="/login" />}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
function BillsIsland() {
const isAdmin = useProtected();
// wait until `isAdmin()` switches to `true` to render `children`
return (
<Show when={isAdmin()} fallback={<Navigate href="/login" />}>
<QueryClientProvider client={queryClient}>
<Bills />
<SolidQueryTools />
</QueryClientProvider>
</Show>
);
}
So put Astro's navigate inside a Navigate component which returns null.
GitHub
solid-router/src/components.tsx at 3c214ce2ceb9b7d9d39d143229a8c614...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
edard3v 🦀
edard3v 🦀OP6d ago
Indeed, I'll put it up later, it's cool.
edard3v 🦀
edard3v 🦀OP6d ago
It works spectacularly xD. Thanks @peerreynders
No description
No description
edard3v 🦀
edard3v 🦀OP6d ago
nice good
No description

Did you find this page helpful?