S
SolidJS3mo ago
Faterek

How can I make sure that data from createAsync is loaded, before passing it to another function

No matter what I do dirID is always undefined when it gets to other functions, createEffect also did nothing for me but that might be because I used it incorrectly.
import { createSession } from "@solid-mediakit/auth/client";
import { createAsync, useParams } from "@solidjs/router";
import { Show, For } from "solid-js";
import FileRecord from "@/components/FileRecord";
import {
getDirID,
getFiles,
getSubDirectories,
} from "@/lib/filebrowser-service";

export default function Protected() {
const session = createSession();

const params = useParams()["directory"].split("/");
const ancestors = params.slice(0, -1);
const name = params[params.length - 1];

const dirID = createAsync(async () => {
return JSON.parse(await getDirID(ancestors, name));
});

const subDirs = createAsync(async () => {
return JSON.parse(await getSubDirectories(dirID()));
});

const files = createAsync(async () => {
return JSON.parse(await getFiles(dirID()));
});

return (
<Show
when={session()}
fallback={<p>Only shown for logged in users</p>}
keyed
>
<main class="main-content">
<div class="filemanager-table">
<Show when={subDirs()}>
<For each={subDirs()}>{(subDir) => <FileRecord {...subDir} />}</For>
</Show>
<Show when={files()}>
<For each={files()}>{(file) => <FileRecord {...file} />}</For>
</Show>
</div>
</main>
</Show>
);
}
import { createSession } from "@solid-mediakit/auth/client";
import { createAsync, useParams } from "@solidjs/router";
import { Show, For } from "solid-js";
import FileRecord from "@/components/FileRecord";
import {
getDirID,
getFiles,
getSubDirectories,
} from "@/lib/filebrowser-service";

export default function Protected() {
const session = createSession();

const params = useParams()["directory"].split("/");
const ancestors = params.slice(0, -1);
const name = params[params.length - 1];

const dirID = createAsync(async () => {
return JSON.parse(await getDirID(ancestors, name));
});

const subDirs = createAsync(async () => {
return JSON.parse(await getSubDirectories(dirID()));
});

const files = createAsync(async () => {
return JSON.parse(await getFiles(dirID()));
});

return (
<Show
when={session()}
fallback={<p>Only shown for logged in users</p>}
keyed
>
<main class="main-content">
<div class="filemanager-table">
<Show when={subDirs()}>
<For each={subDirs()}>{(subDir) => <FileRecord {...subDir} />}</For>
</Show>
<Show when={files()}>
<For each={files()}>{(file) => <FileRecord {...file} />}</For>
</Show>
</div>
</main>
</Show>
);
}
7 Replies
Brendonovich
Brendonovich3mo ago
To wait for dirID to load you'd need to have a Show for dirID, and put subDirs etc inside that, so they're only created when dirID exists. Instead I'd do this so that fetching subDirs etc doesn't have to be nested
const subDirs = createAsync(async () => {
const dir = dirId();
if(!dir) return;

return JSON.parse(await getSubDirectories(dir));
});

const files = createAsync(async () => {
const dir = dirId();
if(!dir) return;

return JSON.parse(await getFiles(dir));
});
const subDirs = createAsync(async () => {
const dir = dirId();
if(!dir) return;

return JSON.parse(await getSubDirectories(dir));
});

const files = createAsync(async () => {
const dir = dirId();
if(!dir) return;

return JSON.parse(await getFiles(dir));
});
Faterek
Faterek3mo ago
That still didn't work, with code like this:
export default function Protected() {
const session = createSession();

const params = useParams()["directory"].split("/");
const ancestors = params.slice(0, -1);
const name = params[params.length - 1];

const dirID = createAsync(async () => {
return JSON.parse(await getDirID(ancestors, name));
});

const subDirs = createAsync(async () => {
const dir = dirID();
console.log("subDirs dir is:", dir);
if (!dir) return;
return JSON.parse(await getSubDirectories(dir));
});

const files = createAsync(async () => {
const dir = dirID();
console.log("files dir is:", dir);
if (!dir) return;
return JSON.parse(await getFiles(dir));
});

return (
<Show
when={session()}
fallback={<p>Only shown for logged in users</p>}
keyed
>
<main class="main-content">
<div class="filemanager-table">
<Show when={dirID()}>
<Show when={subDirs()}>
<For each={subDirs()}>
{(subDir) => <FileRecord {...subDir} />}
</For>
</Show>
<Show when={files()}>
<For each={files()}>{(file) => <FileRecord {...file} />}</For>
</Show>
</Show>
</div>
</main>
</Show>
);
}
export default function Protected() {
const session = createSession();

const params = useParams()["directory"].split("/");
const ancestors = params.slice(0, -1);
const name = params[params.length - 1];

const dirID = createAsync(async () => {
return JSON.parse(await getDirID(ancestors, name));
});

const subDirs = createAsync(async () => {
const dir = dirID();
console.log("subDirs dir is:", dir);
if (!dir) return;
return JSON.parse(await getSubDirectories(dir));
});

const files = createAsync(async () => {
const dir = dirID();
console.log("files dir is:", dir);
if (!dir) return;
return JSON.parse(await getFiles(dir));
});

return (
<Show
when={session()}
fallback={<p>Only shown for logged in users</p>}
keyed
>
<main class="main-content">
<div class="filemanager-table">
<Show when={dirID()}>
<Show when={subDirs()}>
<For each={subDirs()}>
{(subDir) => <FileRecord {...subDir} />}
</For>
</Show>
<Show when={files()}>
<For each={files()}>{(file) => <FileRecord {...file} />}</For>
</Show>
</Show>
</div>
</main>
</Show>
);
}
output of console logs is
subDirs dir is: undefined
files dir is: undefined
subDirs dir is: undefined
files dir is: undefined
Brendonovich
Brendonovich3mo ago
Yeah it's always going to be undefined for a bit If you really wanted to wait until it wasn't undefined, move subDirs and files into the <Show when={dirID()}> and use the callback form of Show, that way youll have access to dirID without it being undefined
brenelz
brenelz3mo ago
Out of curiosity how is <Protected/> used? Like is it user in app.tsx?
peerreynders
peerreynders3mo ago
I'd favour resolving async dependencies on the async side; not the reactive side.
Spooky Dooky
Spooky Dooky2mo ago
Can't you do:
const dirID = createAsync(async () => {
return JSON.parse(await getDirID(ancestors, name));
});

const subDirs = createAsync(
dirId,
async (d) => {return JSON.parse(await getSubDirectories(d));}
);

const files = createAsync(
dirId,
async (d) => {return JSON.parse(await getFiles(d));}
);
const dirID = createAsync(async () => {
return JSON.parse(await getDirID(ancestors, name));
});

const subDirs = createAsync(
dirId,
async (d) => {return JSON.parse(await getSubDirectories(d));}
);

const files = createAsync(
dirId,
async (d) => {return JSON.parse(await getFiles(d));}
);
As long as dirId evaluates to undefined, the fetch doesn't trigger. But once the first async resolves, it will? At least that is how createResource used to work. Is there a reason for this feature to be removed?
peerreynders
peerreynders2mo ago
You're thinking createResource() for the last two. createAsync() doesn't have that signature. To get a createAsync() to refetch you revalidate() the cache (key) it consumes. One advantage of the createAsync/cache split is that every component can have its own createAsync() that share the same cache() wrapper. With createResource() everybody shares the same accessor or each component with a createResource will cause its own async op (e.g. fetch). The latter is undesirable because - multiple fetches - separate components can be at different levels of staleness.
GitHub
solid-router/src/data/createAsync.ts at 098a7947ae24f85d05bde76bf26...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
Want results from more Discord servers?
Add your server