blob: 30a564c206cea17021fa889f0e35107be42b0010 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
// A component whose code is fetched the first time it is shown.
//
// Every static `import` reachable from app.js is downloaded before anything
// renders, whether or not the visitor ever opens it: the Videos app, the video
// player and the search page used to cost a member who only chats. Wrapping a
// component here moves its module out of that first download and into the
// moment it is needed; once loaded it is the component itself, rendered with
// the same props, and every later mount is immediate.
//
// A failed fetch (a network drop on a phone) is not remembered: the next time
// the component is shown it asks again, rather than failing for the session.
import { html, useState, useEffect } from './vendor/htm-preact.js';
const SPINNER = html`<p class="page-message"><span class="spinner"></span></p>`;
function lazy(load, name, placeholder = SPINNER) {
let Loaded = null;
let pending = null;
function Lazy(props) {
const [, setReady] = useState(Loaded !== null);
useEffect(() => {
if (Loaded) return undefined;
let alive = true;
if (!pending) {
pending = load().then(
(m) => { Loaded = m[name]; },
(e) => { pending = null; throw e; });
}
pending.then(() => { if (alive) setReady(true); }, () => {});
return () => { alive = false; };
}, []);
if (!Loaded) return placeholder;
return html`<${Loaded} ...${props} />`;
}
Lazy.displayName = `Lazy(${name})`;
return Lazy;
}
export { lazy };
|