jack
jack
Explore posts from servers
SSolidJS
Created by jack on 12/15/2024 in #support
dealing with data shared between pages
this kind of seems like a common question among meta frameworks w/ routing built in of, "how do i share data from one page to the next". in this case, the cache doesn't help as this really only dedups for a single round trip more or less the scenario i'm dealing with, which i would appreciate advice, is follows: i have a dashboard page where i fetch all of the groups the user belongs to, and i display them as cards. if the user clicks a card, it brings them to /groups/[groupId]. here i render out a heading with the group name (which of course i already fetched on the previous page) and then all the rest of the page, which takes a bit to render due to fetching im waiting on. it is a nice touch if the heading renders while the rest of the page loads via suspense. this seems like a no brainer as i already have the data i wanna show.. i just don't know the best way to get it where i need it my current hack is, on pointerdown of the cards that trigger nav, i update a global object like so navigation.lastClickedGroupName = group.name, and then i read that on the next page. i originally was using a global signal, but i realized i don't think i really need reactivity, so just went to a regular object my question is, this feels super hacky. it's starting to feel like a global store is kind of what i ultimately need. is that the solution here since i have significant state needs across pages? basically just want to know what would be a best practice
38 replies
SSolidJS
Created by jack on 12/15/2024 in #support
weird history/url derived state behavior when using mouse back button
kind of a weird issue. basically i have a modal/dialog for which i derive open/close state from the url as the source the general idea is in a hook i grab the search params, and then return a memo'd state method that parses the params and returns whether or not the dialog is open based on that. then some open/close methods too. it was seemingly working really nicely and i thought was an elegant solution, ux and dx. however, i'm noticing that the back button i have on my mouse, when trying to go back to closed state, does not work. the modal closes on button down, and then comes back on button up. i have know idea what event is even happening when this happens. it might just be some weird behavior w/ my mouse/computer, but i thought it's a good excuse to ask for a review on my hook for the dialog state. if someone could let me know if something looks clearly broken, it would be greatly appreciated
function useDialogState() {
const [searchParams, setSearchParams] = useSearchParams<{
action?: 'new-transaction';
}>();

const state = createMemo<DialogState>(() =>
searchParams.action === 'new-transaction' ? 'open' : 'closed',
);

const open = () => {
setSearchParams({ action: 'new-transaction' });
};

const close = (onClose?: () => void) => {
onClose?.();
setSearchParams({ ...searchParams, action: undefined });
};

const toggle = () => {
(state() === 'open' ? close : open)();
};

const context = {
state,
open,
close,
toggle,
};

return context;
}
function useDialogState() {
const [searchParams, setSearchParams] = useSearchParams<{
action?: 'new-transaction';
}>();

const state = createMemo<DialogState>(() =>
searchParams.action === 'new-transaction' ? 'open' : 'closed',
);

const open = () => {
setSearchParams({ action: 'new-transaction' });
};

const close = (onClose?: () => void) => {
onClose?.();
setSearchParams({ ...searchParams, action: undefined });
};

const toggle = () => {
(state() === 'open' ? close : open)();
};

const context = {
state,
open,
close,
toggle,
};

return context;
}
2 replies
SSolidJS
Created by jack on 11/30/2024 in #support
blank page on error
on occasion if there's an error that doesn't pop up in console or on my server, the route will just appear blank, with only the parent layouts rendering. this happens somewhat frequently as i'm newer to solid, and debugging it is a pain. i don't really know how to better explain it than that somewhere in the filetree my components just stop rendering but no errors show up anywhere i can send a screen recording if it'd help, but just wondering if this sounds like a familiar bug or something that others have run into
21 replies
SSolidJS
Created by jack on 11/24/2024 in #support
using action/useSubmission, discern which button submitted the action?
i have a form with a create and a create & new, (the latter just creates the entity without clearing form/redirect/etc.) the issue is, i don't want to render a loading indicator on both buttons every submission, only the button that created the submission. is there a way to discern which caused the submission? i was thinking an extra param to the function, but it feels a little hacky as the param isn't actually used. open to any thoughts! thanks
19 replies
DTDrizzle Team
Created by jack on 11/8/2024 in #help
is it possible for a join table to have one of the relations reference a composite key?
i have the following: 1. contacts table - userId // this references the user's id per clerk (otherwise, this would really be basically a join table) - contactId // this references the contact's clerk user id - pk is composite of the aforementioned ids so if i want all contacts, i can just select from here filtering on userid, and then fill in the contact data later on from clerk 2. transactions table - id, pk - payerId // this is the userId, or the person who paid, referencing clerkId and - whole bunch of other stuff regarding the transaction 3. join table so that i can grab every contact involved in a transaction, i have a table to join the aforementioned i think i need relations as follows contacts -> many transactions transactions -> many contacts join table one -> contact one -> transaction the issue i'm running into is that the contact table is using a composite key, and so i don't know what to reference in the join table relation here. it should be the combination of both, but drizzle doesn't seem to let me pass multiple args here and it seems to expect one does this model of schemas make sense or am i misunderstanding something? or is there some level of docs i'm missing out on
1 replies
SSolidJS
Created by jack on 10/28/2024 in #support
is middleware strictly for server?
basically title ^- is the middleware basically meant for server functions/api routes? eg. if i'm running with ssr: false, this is basically only gonna run on page load when the dev server serves the app right? i'm using the clerk-solid package to integrate clerk and would love to find a way to avoid attaching my auth token on every request, but not sure if this is possible
4 replies
SSolidJS
Created by jack on 9/27/2024 in #support
subroute not rendering on client nav
I recently started a new branch to add a 3rd party Clerk solid integration to my app, and all of a sudden, all pages that are children of one layout won't render on client nav. i assumed something was wrong with the new dep i added, but i got rid of all references to that and then created a new page in this sub layout, but this also fails to render on client nav. they always all work on full page reload. i assume that it's due to the layout in question, though it looks entirely normal to me (the other layout's subpages render fine on client nav, which is why i think this is the root of the issue)
export default function RootLayout(props: RouteSectionProps) {
const params = useParams<{ username: string }>();
onMount(destroyToasters);

return (
<main class="z-10 flex flex-col px-4 pt-8 pb-16 mx-auto text-center gap-5 sm:min-w-96 w-full max-w-screen-md">
<Show when={useLocation().pathname} keyed>
<Suspense
fallback={<UsernamePageSkeleton username={params.username} />}
>
{props.children}
</Suspense>
</Show>
<Toaster regionId="app-toaster" />
</main>
);
}
export default function RootLayout(props: RouteSectionProps) {
const params = useParams<{ username: string }>();
onMount(destroyToasters);

return (
<main class="z-10 flex flex-col px-4 pt-8 pb-16 mx-auto text-center gap-5 sm:min-w-96 w-full max-w-screen-md">
<Show when={useLocation().pathname} keyed>
<Suspense
fallback={<UsernamePageSkeleton username={params.username} />}
>
{props.children}
</Suspense>
</Show>
<Toaster regionId="app-toaster" />
</main>
);
}
honestly have no clue what's going on here and it's super difficult to debug this it seems, nothing interesting is really happening in console.
8 replies
SSolidJS
Created by jack on 9/24/2024 in #support
how to wrap one of the control components
preface: this is probably a silly idea to begin with, but i started trying to implement this and now just want to see it through basically, between actual conditions, and just guarding against async values, i've ended up with a lot of <Show/> on my page. i had an idea to basically wrap <Show /> to create a new component <Suspending />, and instead of taking fallback it takes skeleton as i'm using loading skeletons for fallback on async values. (like i said, i know this is kind of silly as im basically trying to just rename the component). would marginally help me figure out what i'm using as async guard, and what i'm using for other state like mapping a list vs showing empty array state. i tried to implement it as follows by just taking the prop type defs from the Show component (and then i would handle renaming to my liking once i have it working), but the types are not making it through and often becoming any or {}
type SuspendingProps<T> = {
when: Parameters<typeof Show>[0]["when"];
fallback: Parameters<typeof Show>[0]["fallback"];
children: Parameters<typeof Show>[0]["children"];
};

function Suspending<T>(props: SuspendingProps<T>) {
return <Show {...props} />;
}
type SuspendingProps<T> = {
when: Parameters<typeof Show>[0]["when"];
fallback: Parameters<typeof Show>[0]["fallback"];
children: Parameters<typeof Show>[0]["children"];
};

function Suspending<T>(props: SuspendingProps<T>) {
return <Show {...props} />;
}
when i use it like this, the type of user doesn't really make it through and i get type errors
<Suspending
when={user}
fallback={<HeaderSkeleton username={params.username} />}
>
{(user) => <Header user={user()} view={view()} />}
</Suspending>
<Suspending
when={user}
fallback={<HeaderSkeleton username={params.username} />}
>
{(user) => <Header user={user()} view={view()} />}
</Suspending>
anyone got ideas? I was thinking maybe generics could help which is why I set up the T type param on the fn, but i couldn't figure out how to wire that up properly
6 replies
SSolidJS
Created by jack on 8/26/2024 in #support
useSubmission pending state resolves on url change?
i have some optimistic ui, with roughly the following structure:
view = "edit" | "read", based on searchParams
data = fetch data from server
editable = copy of data we can write to (what we render)

when the user clicks save =>
invoke save fn() (which is an action())
**set view -> "read" (rest of ui is already optimistically set)

if no error thrown (via try)
await revalidate() // wan't to make sure we have server state befoe confirming success
toast("good")
if error thrown (via catch)
await revalidate() // reset to good state
toast("bad")

view = "edit" | "read", based on searchParams
data = fetch data from server
editable = copy of data we can write to (what we render)

when the user clicks save =>
invoke save fn() (which is an action())
**set view -> "read" (rest of ui is already optimistically set)

if no error thrown (via try)
await revalidate() // wan't to make sure we have server state befoe confirming success
toast("good")
if error thrown (via catch)
await revalidate() // reset to good state
toast("bad")

then somewhere else i use useSubmission(save) and disable buttons based on pending state as i don't want user's editing lists that aren't accurate to server state. the weird thing is, when i include the logic above with the **, the useSubmission state basically dies. it returns to undefined, and my buttons are don't disabled if instead i wait to set view -> "read" until after i've checked the result of the server call, then the pending state works perfectly (which i can't really do, otherwise the illusion of the optimistic ui is gone) wondering if this is expected, or perhaps i'm doing something wrong? I can post code tomorrow if this doesn't make sense, thanks!
6 replies
SSolidJS
Created by jack on 8/26/2024 in #support
does any sort of primitive similar to Show for non-jsx use exist?
i had Claude generate most of this function for me. wondering if there's a solution/pattern for this problem, as well as if this is a horrible idea and i'm missing some case as to why
export function depends<T, U>(
v: () => T | undefined,
fn: (value: NonNullable<T>) => Promise<U>
): () => Promise<U | undefined> {
return async () => {
const value = v();
if (value !== undefined && value !== null) {
return fn(value as NonNullable<T>);
}
return undefined;
};
}
export function depends<T, U>(
v: () => T | undefined,
fn: (value: NonNullable<T>) => Promise<U>
): () => Promise<U | undefined> {
return async () => {
const value = v();
if (value !== undefined && value !== null) {
return fn(value as NonNullable<T>);
}
return undefined;
};
}
so that i can write signals with async inputs a bit easier:
const selections = createAsync(
depends(
() => user()?.id,
async (userId) => getSelections(userId)
)
);
const selections = createAsync(
depends(
() => user()?.id,
async (userId) => getSelections(userId)
)
);
previously i'd have to either do some sort of type guard/narrow manually each time in the function passed here which isn't great, or I just do like user()?id ?? "", but i don't really want to be passing empty string down as a fallback, i'd rather do nothing until user()?.id is valid
1 replies
SSolidJS
Created by jack on 8/25/2024 in #support
use of cache causing page not to render at all on client nav
been struggling with this for like 2 hours and finally narrowed down that if any single cache() call exists in my file, my page just won't render it's content (at least on client) i have no idea why this is happening, and i'm not really sure at what point this began to happen. wondering if there are any common symptoms for this
4 replies
SSolidJS
Created by jack on 8/25/2024 in #support
derived, but update-able, signal?
is it possible to have a piece of reactive code that can be updated directly, but is also tied to another piece of state? I have a list I fetch from server that is wrapped with createAsync. I want to use this to track server state, but then have a dup of that list that can be updated any number of times in the app. The user then hits save, and we update the list on server and revalidate I originally just tried a derived signal, but I can't directly update this. Then I was thinking of just making another signal and having it's initial value be a call to its initial value, but I'm not sure if this is a good thing to do any idea?
47 replies
SSolidJS
Created by jack on 8/10/2024 in #support
typing Page props ?
is it possible to type the props for the page? I'm tryna to block the page until render, and the best I've come up with is:
const load = cache(
(userId: string) => api.getSelectionsByUserId(userId),
"selections"
);

export const route = {
preload: () => load,
} satisfies RouteDefinition;

export default function Username(props: ManuallyTypePropsAfterLookingAtConsoleLog) {
const selections = createAsync(() => load(users[0].id), {
deferStream: true,
initialValue: props.data,
});
//...
const load = cache(
(userId: string) => api.getSelectionsByUserId(userId),
"selections"
);

export const route = {
preload: () => load,
} satisfies RouteDefinition;

export default function Username(props: ManuallyTypePropsAfterLookingAtConsoleLog) {
const selections = createAsync(() => load(users[0].id), {
deferStream: true,
initialValue: props.data,
});
//...
but the props have to be manually typed
39 replies
SSolidJS
Created by jack on 8/9/2024 in #support
nested layout
is it possible to make the file structure like follows:
- routes
- (user)
- layout.tsx
- [username].tsx
- routes
- (user)
- layout.tsx
- [username].tsx
obviously layout.tsx isn't the proper naming convention, but just wondering if it's possible to achieve something like that. Currently I'm doing
- routes
- (user)
- [username].tsx
- (user).tsx // this is the layout
- routes
- (user)
- [username].tsx
- (user).tsx // this is the layout
but routes/ is gets pretty cluttered fast as I create route groups
3 replies
TTCTheo's Typesafe Cult
Created by jack on 7/26/2024 in #questions
<img /> flicker on transition when conditionally rendering separate lists
No description
2 replies
TTCTheo's Typesafe Cult
Created by jack on 3/17/2024 in #questions
revalidate cached data on page load app router?
if i fetch a user's profile data with unstable_cache, but in case a user updates their data since last cache, i want to get that update data. is their a way (i believe RQ worked this way) so that you rely on the cached data on page load, but then it refetches in the background and updates if it's new? i came up with this seemingly really hacky client comp that will revalidate the path on page load. i just mount it in the page component. wondering if anyone has a better approach. I might just default to not using unstable_cache, and just always fetch new data
"use client";

import { useEffect, useMemo } from "react";

const useRevalidateOnPageLoad = (revalidate: () => void) => {
useEffect(() => {
console.log("running");
(() => {
revalidate();
})();
}, []);
};

export const RevalOnLoad = (props: { reval: () => void }) => {
const reval = useMemo(() => props.reval, [props.reval]);
useRevalidateOnPageLoad(reval);

return null;
};
"use client";

import { useEffect, useMemo } from "react";

const useRevalidateOnPageLoad = (revalidate: () => void) => {
useEffect(() => {
console.log("running");
(() => {
revalidate();
})();
}, []);
};

export const RevalOnLoad = (props: { reval: () => void }) => {
const reval = useMemo(() => props.reval, [props.reval]);
useRevalidateOnPageLoad(reval);

return null;
};
3 replies
TTCTheo's Typesafe Cult
Created by jack on 2/24/2024 in #questions
app architecture help?
No description
2 replies
TTCTheo's Typesafe Cult
Created by jack on 12/27/2023 in #questions
discriminated union as props
is there anything noticeably wrong with this
type SidebareOpenContentsProps = {
showSkeletons: boolean;
sidebarItems: MockSidebarItems | {};
} & (
| {
showSkeletons: true;
sidebarItems: {};
}
| {
showSkeletons: false;
sidebarItems: MockSidebarItems;
}
);
type SidebareOpenContentsProps = {
showSkeletons: boolean;
sidebarItems: MockSidebarItems | {};
} & (
| {
showSkeletons: true;
sidebarItems: {};
}
| {
showSkeletons: false;
sidebarItems: MockSidebarItems;
}
);
, when used like
<SidebarOpenContents
sidebarItems={status === "success" ? sidebarItems : {}}
showSkeletons={status === "loading"}
/>
<SidebarOpenContents
sidebarItems={status === "success" ? sidebarItems : {}}
showSkeletons={status === "loading"}
/>
I'm getting a "false is not assignable to boolean" error, but I thought this would be the only way to use discrimnated union to type narrow properly here.
2 replies
SSolidJS
Created by jack on 12/22/2023 in #support
type narrowing signals
I've got some state (I'm making a card game) that looks like
type GameInProgress = {
state: "playing" | "paused";
p1Flipped: boolean;
p2Flipped: boolean;
};

type GameOver = {
state: "over";
};

type GamePaused = {
state: "paused";
p1Flipped: true;
p2Flipped: true;
};

type Game = {
state: "playing" | "paused" | "over";
} & (GameInProgress | GamePaused | GameOver);
type GameInProgress = {
state: "playing" | "paused";
p1Flipped: boolean;
p2Flipped: boolean;
};

type GameOver = {
state: "over";
};

type GamePaused = {
state: "paused";
p1Flipped: true;
p2Flipped: true;
};

type Game = {
state: "playing" | "paused" | "over";
} & (GameInProgress | GamePaused | GameOver);
and then my signal to track game state ends up looking like
const [gameState, setGameState] = createSignal<Game>({
state: "over",
});
const [gameState, setGameState] = createSignal<Game>({
state: "over",
});
over time, i want to update this object. since i'm using discriminated types, if i want to reason about any of the props that only exist on GamePaused or GameOver, I need to do some narrowing. it seems like doing a if (gameState().state !== "playing") { return } ... gameState().___ doesn't successfully autocomplete. I'm assuming that typescript doesn't have enough info to guarantee that the 2nd getter invocation will be equivalent to that of the first. Next reasonable idea is to invoke the getter once and store in a variable, then use that to type narrow. But then when I do a setGameState(...) using that variable, I'm not following the nested reactivity pattern here https://www.solidjs.com/tutorial/stores_nested_reactivity Any thoughts/best practices here ?
16 replies
TTCTheo's Typesafe Cult
Created by jack on 11/12/2023 in #questions
type wizard help needed for my fake trpc package
I kinda gave up on tRPC with app router cuz I got frustrated BUT I really like using tRPC. I appreciate that it's organized (but not too restrictive like a rest server) and the method by which inputs are passed and parsed. As such, I started working on a way in my app to define things almost exactly like a trpc router/procedure so that you still get input parsing with zod, type safety updates on definition and caller side, but just without the trpc stuff. At this point it's not even worth it, but I'm too invested and want it to work. This is majority of the code
type AsyncQuillQueryWithSchema<T> = (input: T) => Promise<unknown>;
type AsyncQuillQueryNoSchema = () => Promise<unknown>;

function createQuillRoute() {
const input = <T extends z.ZodTypeAny>(schema: T) => {
// define a fn that takes a query fn and returns a fn that parses it's input first
const typeSafeQueryInvoker = (
fn: AsyncQuillQueryWithSchema<z.infer<T>>,
) => {
return async (input: z.infer<typeof schema>) => {
try {
await schema.parseAsync(input);
return await fn(input);
} catch (error) {
throw error;
}
};
};

// return an object on that which is call-able
return {
query: typeSafeQueryInvoker,
// mutation: typeSafeMutationInvoker,
};
};

// we also want to define a query fn that doesn't take any input
const query = (fn: AsyncQuillQueryNoSchema) => {
return async () => fn();
};

return {
input,
query,
};
}

export const quill = () => ({
...createQuillRoute(),
});
type AsyncQuillQueryWithSchema<T> = (input: T) => Promise<unknown>;
type AsyncQuillQueryNoSchema = () => Promise<unknown>;

function createQuillRoute() {
const input = <T extends z.ZodTypeAny>(schema: T) => {
// define a fn that takes a query fn and returns a fn that parses it's input first
const typeSafeQueryInvoker = (
fn: AsyncQuillQueryWithSchema<z.infer<T>>,
) => {
return async (input: z.infer<typeof schema>) => {
try {
await schema.parseAsync(input);
return await fn(input);
} catch (error) {
throw error;
}
};
};

// return an object on that which is call-able
return {
query: typeSafeQueryInvoker,
// mutation: typeSafeMutationInvoker,
};
};

// we also want to define a query fn that doesn't take any input
const query = (fn: AsyncQuillQueryNoSchema) => {
return async () => fn();
};

return {
input,
query,
};
}

export const quill = () => ({
...createQuillRoute(),
});
which can then be used like this (where i nest it a few times)
const profile = await quillApi.user.getUserByProfileId({
username: props.params.username,
});
const profile = await quillApi.user.getUserByProfileId({
username: props.params.username,
});
this works surprisingly well, however, it's not inferring return type properly. I just get unknown, which makes sense since my fn types define the return as such. Anyone have thoughts?
3 replies