Suspense
Suspense demo built with Expressive MVC - the complete source below runs as an editable sandbox when JavaScript is enabled.
App.tsx
import './App.css';
import Button from './common/Button';
import { Component, get, set } from '@expressive/react';
export default () => (
<div className="container">
<h1>Suspense</h1>
<p>
An async <code>set</code> factory resolves straight into its field, and
reading it while pending suspends the render. Every Component carries a
boundary for that, so <code>fallback</code> is the whole of the wiring -
there is no <code><Suspense></code> anywhere in this file.
</p>
<Demo />
<small>
The second card declines its own boundary with{' '}
<code>{'fallback={false}'}</code>, so Panel covers it. That works only
because Panel owns the pending value: a boundary rebuilds the subtree it
retries, so state owned below would be rebuilt and requested again forever.
</small>
</div>
);
class Demo extends Component {
round = 0;
render() {
const { round } = this;
return (
<>
<div className="card">
<h2>Its own boundary</h2>
<Greeter key={`own-${round}`} name="Ada" />
</div>
<div className="card">
<h2>Deferred to an ancestor</h2>
<Panel key={`panel-${round}`}>
<Reader fallback={false} />
</Panel>
</div>
{/* An async factory resolves once, so a fresh request means a fresh
instance - which is what keying the owner asks for. */}
<Button primary onClick={() => this.round++}>
Ask again
</Button>
</>
);
}
}
class Greeter extends Component {
name = 'world';
greeting = set(async () => {
await wait(900);
return `Hello, ${this.name}.`;
});
fallback = <small>Greeting someone…</small>;
render() {
return <p className="result">{this.greeting}</p>;
}
}
class Panel extends Component {
farewell = set(async () => {
await wait(1400);
return 'Goodbye, Grace.';
});
fallback = <small>The panel is waiting…</small>;
}
class Reader extends Component {
panel = get(Panel);
render() {
return <p className="result">{this.panel.farewell}</p>;
}
}
const wait = (ms: number) => new Promise((done) => setTimeout(done, ms));
App.css
.card {
width: 100%;
padding: var(--s4);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--r);
}
.card h2 {
font-size: var(--t-sm);
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.04em;
margin-bottom: var(--s3);
}
.result {
margin: 0;
font-size: var(--t-lg);
color: var(--fg);
}