Prerendering async data #950
-
| 
         Similar to the question asked in #305 I have a collection of posts/entries coming from an API, while developing I am happy for them to be requested real time but ideally I would like to build them statically for production using prerender. Is there a built in way to do this? I did take a look at   | 
  
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
| 
         The docs site works by patching  Lines 7 to 11 in 3c5672e This has become built-in as of #934 (as it's pretty handy) though, so you won't need to do that manually if that's the behavior you want. Fetching a local file (using a relative specifier, like  However, for external data sources (as it sounds like you have), you can lightly modify  import { useState } from 'preact/hooks';
const CACHE = new Map();
async function load(url) {
	const res = await fetch(url);
        const { html } = await res.json();
	return { html };
}
export function useContent(url) {
	let update = useState(0)[1];
	let p = CACHE.get(url);
	if (!p) {
		p = load(url);
		CACHE.set(url, p);
		p.then(
			v => update((p.v = v)),
			e => update((p.e = e))
		);
	}
	if (p.v) return p.v;
	if (p.e) throw p.e;
	throw p;
}Then in our component: function MyComponent() {
    const { html } = useContent('https://example.com/some-resource');
}
 Now,  Hope this helps, let me know if you have any issues.  | 
  
Beta Was this translation helpful? Give feedback.
-
| 
         Okay this is incredible and absolutely works, could I be a pain and check my understanding as to why  Is it because we  The documentation definitely hints at this: 
 I just had no idea this could be extended to hooks as well.  | 
  
Beta Was this translation helpful? Give feedback.
-
| 
         For anyone who comes across this thread in the future here is a generic hook I've created to do this with any promise. import { useState } from "preact/hooks";
const CACHE = new Map();
export const usePromise = <T = any>(
    key: string,
    handler: (key?: string) => Promise<T>
) => {
    const [_, update] = useState<unknown>(null);
    let value = CACHE.get(key);
    if (value === undefined) {
        value = handler(key);
        CACHE.set(key, value);
        value.then((res: unknown) => update((value.res = res)));
    }
    if (value.res) {
        return value.res as Awaited<ReturnType<typeof handler>>;
    }
    throw value;
};It can then be used like: const response = usePromise('some-key', async () => {
    const resp = await fetch('https://...');
    return resp.json();
});Or even create some additional hooks from that base hook: const useFetch = (url: string) => usePromise(url, async () => {
    const resp = await fetch(url);
    return resp.json();
});Haven't run into any issues yet.  | 
  
Beta Was this translation helpful? Give feedback.
The docs site works by patching
fetch()to afs.readFile()implementation, which you can find here:wmr/docs/public/prerender.js
Lines 7 to 11 in 3c5672e
This has become built-in as of #934 (as it's pretty handy) though, so you won't need to do that manually if that's the behavior you want. Fetching a local file (using a relative specifier, like
fetch('/foo.js')) will automatically usefswhen prerendering.However, for exter…