Skip to content

Instantly share code, notes, and snippets.

@intrnl
Last active December 17, 2023 07:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save intrnl/3edc9633a74519eebc08bde93d44e405 to your computer and use it in GitHub Desktop.
Save intrnl/3edc9633a74519eebc08bde93d44e405 to your computer and use it in GitHub Desktop.
Freezing component subtrees using Suspense in Solid.js
import { type JSX, Suspense, createMemo, createResource, For } from 'solid-js';
export interface FreezeProps {
freeze: boolean;
children: JSX.Element;
fallback?: JSX.Element;
}
type Deferred = Promise<undefined> & { r: (value: undefined) => void };
const identity = <T,>(value: T): T => value;
export const useSuspend = (freeze: () => boolean) => {
const promise = createMemo((prev: Deferred | undefined) => {
if (freeze()) {
if (prev) {
return prev;
}
let _resolve: Deferred['r'];
let promise = new Promise((resolve) => (_resolve = resolve)) as Deferred;
promise.r = _resolve!;
return promise;
} else if (prev) {
prev.r(undefined);
}
});
const [suspend] = createResource(promise, identity);
return suspend;
};
export const Freeze = (props: FreezeProps) => {
const suspend = useSuspend(() => props.freeze);
return (
<Suspense
fallback={props.fallback}
// @ts-expect-error
children={[suspend, props.children]}
/>
);
};
export interface ShowFreezeProps {
when: boolean;
children: JSX.Element;
fallback?: JSX.Element;
}
export const ShowFreeze = (props: ShowFreezeProps) => {
// Hard-stuck to `true` if `props.when` is true
const show = createMemo((prev: boolean) => {
if (prev || props.when) {
return true;
}
return prev;
}, false);
return Freeze({
get freeze() {
return show() && !props.when;
},
get children() {
if (show()) {
return props.children;
}
},
});
};
export interface KeepAliveProps<T extends string> {
key: T;
render: (id: T) => JSX.Element;
}
export const KeepAlive = <T extends string>(props: KeepAliveProps<T>) => {
const keys = createMemo<T[]>((prev: T[]) => {
const key = props.key;
if (!prev.includes(key)) {
return [...prev, key];
}
return prev;
}, []);
return (
<For each={keys()}>
{(key) => {
const frozen = useSuspend(() => key !== props.key);
const result = props.render(key);
return <Suspense children={[frozen as unknown as JSX.Element, result]} />;
}}
</For>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment