diff --git a/README.md b/README.md
index 152515d..e7bcbe5 100644
--- a/README.md
+++ b/README.md
@@ -205,11 +205,9 @@ Make a lazily-loaded version of a Component.
import { lazy, LocationProvider, Router } from 'preact-iso';
// Synchronous, not code-splitted:
-// import Home from './routes/home.js';
-// import Profile from './routes/profile.js';
+import Home from './routes/home.js';
// Asynchronous, code-splitted:
-const Home = lazy(() => import('./routes/home.js'));
const Profile = lazy(() => import('./routes/profile.js'));
const App = () => (
@@ -222,6 +220,20 @@ const App = () => (
);
```
+The result of `lazy()` also exposes a `preload()` method that can be used to load the component before it's needed for rendering. Entirely optional, but can be useful on focus, mouse over, etc. to start loading the component a bit earlier than it otherwise would be.
+
+```js
+const Profile = lazy(() => import('./routes/profile.js'));
+
+function Home() {
+ return (
+ Profile.preload()}>
+ Profile Page -- Hover over me to preload the module!
+
+ );
+}
+```
+
### `ErrorBoundary`
A simple component to catch errors in the component tree below it.
diff --git a/src/lazy.d.ts b/src/lazy.d.ts
index 829cdf3..24e3b1b 100644
--- a/src/lazy.d.ts
+++ b/src/lazy.d.ts
@@ -1,5 +1,7 @@
import { ComponentChildren, VNode } from 'preact';
-export default function lazy(load: () => Promise<{ default: T } | T>): T;
+export default function lazy(load: () => Promise<{ default: T } | T>): T & {
+ preload: () => Promise;
+};
export function ErrorBoundary(props: { children?: ComponentChildren; onError?: (error: Error) => void }): VNode;
diff --git a/src/lazy.js b/src/lazy.js
index 9670e8d..564cdae 100644
--- a/src/lazy.js
+++ b/src/lazy.js
@@ -3,14 +3,25 @@ import { useState, useRef } from 'preact/hooks';
export default function lazy(load) {
let p, c;
- return props => {
+
+ const loadModule = () =>
+ load().then(m => (c = (m && m.default) || m));
+
+ const LazyComponent = props => {
const [, update] = useState(0);
const r = useRef(c);
- if (!p) p = load().then(m => (c = (m && m.default) || m));
+ if (!p) p = loadModule();
if (c !== undefined) return h(c, props);
if (!r.current) r.current = p.then(() => update(1));
throw p;
};
+
+ LazyComponent.preload = () => {
+ if (!p) p = loadModule();
+ return p;
+ }
+
+ return LazyComponent;
}
// See https://github.com/preactjs/preact/blob/88680e91ec0d5fc29d38554a3e122b10824636b6/compat/src/suspense.js#L5
diff --git a/test/lazy.test.js b/test/lazy.test.js
new file mode 100644
index 0000000..d16015f
--- /dev/null
+++ b/test/lazy.test.js
@@ -0,0 +1,45 @@
+import { h, render } from 'preact';
+import * as chai from 'chai';
+import * as sinon from 'sinon';
+import sinonChai from 'sinon-chai';
+
+import { LocationProvider, Router } from '../src/router.js';
+import lazy from '../src/lazy.js';
+
+const expect = chai.expect;
+chai.use(sinonChai);
+
+describe('lazy', () => {
+ let scratch;
+
+ beforeEach(() => {
+ if (scratch) {
+ render(null, scratch);
+ scratch.remove();
+ }
+ scratch = document.createElement('scratch');
+ document.body.appendChild(scratch);
+ history.replaceState(null, null, '/');
+ });
+
+
+ it('should support preloading lazy imports', async () => {
+ const A = () => A
;
+ const loadB = sinon.fake(() => Promise.resolve(() => B
));
+ const B = lazy(loadB);
+
+ render(
+
+
+
+
+
+ ,
+ scratch
+ );
+
+ expect(loadB).not.to.have.been.called;
+ await B.preload();
+ expect(loadB).to.have.been.calledOnce;
+ });
+});