SeqFlow Component are asynchronous functions.
Your component can handle async operations in a clear way, just using await
operator.
import { Contexts, ComponentProps, start } from '@seqflow/seqflow';
// component properties
interface MyComponentProps {
loadingText?: string;
}
// A simple component function
export async function MyComponent(
// component properties
{ loadingText }: ComponentProps<MyComponentProps>,
// component context
{ component }: Contexts
) {
// Render loader
component.renderSync(<div>{loadingText ?? 'Loading...'}</div>);
const data = await fetch('https://quotes.seqflow.dev/api/quotes/random').then(
async (res) => ({
statusCode: res.status,
body: await res.json(),
})
);
// Redraw the whole component
component.renderSync(
<pre>
<code>{JSON.stringify(data, null, 2)}</code>
</pre>
);
}
start(document.getElementById('root')!, MyComponent, {}, {});
See live example
SeqFlow component uses modern Javascript statements like for await
to handle events
import { Contexts, ComponentProps, start } from '@seqflow/seqflow';
async function MyComponent(
{}: ComponentProps<unknown>,
{ component }: Contexts
) {
component.renderSync(
<button key="my-button" type="button">
Click me
</button>
);
// create AsyncGenerator
const events = component.waitEvents(component.domEvent('my-button', 'click'));
// Wait for events
for await (const ev of events) {
window.alert('Button clicked: ' + ev.type);
}
}
start(document.getElementById('root')!, MyComponent, {}, {});
See live example
SeqFlow uses simple Javascript variable to store component data.
You can use number, string, array, object or class instances, as simple as it should be.
import { Contexts, ComponentProps, start } from '@seqflow/seqflow';
async function MyComponent(
{}: ComponentProps<unknown>,
{ component }: Contexts
) {
// The state is a simple Javascript variable
let counter = 0;
component.renderSync(
<button key="my-button" type="button">
Click me
</button>
);
const events = component.waitEvents(component.domEvent('my-button', 'click'));
for await (const ev of events) {
// Update the counter
counter++;
window.alert('Number of click:' + counter);
}
}
start(document.getElementById('root')!, MyComponent, {}, {});
See live example
SeqFlow component can re-render the whole component or just perform a partial update
You can name a child to refer to it later
import { Contexts, ComponentProps, start } from '@seqflow/seqflow';
async function MyComponent(
{}: ComponentProps<unknown>,
{ component }: Contexts
) {
component.renderSync(
<>
<button key="my-button" type="button">
Now
</button>
<div key="counter">{new Date().toISOString()}</div>
</>
);
const events = component.waitEvents(component.domEvent('my-button', 'click'));
for await (const ev of events) {
// Replace the counter div element
component.replaceChild('counter', () => (
<div key="counter">{new Date().toISOString()}</div>
));
}
}
start(document.getElementById('root')!, MyComponent, {}, {});
See live example
"Talk is cheap. Show me the code."
Linus Torvalds
// Imports
import { Contexts, ComponentProps, start } from '@seqflow/seqflow';
import { Button } from '@seqflow/components';
import '@seqflow/components/style.css';
interface CounterProps {
initialValue?: number;
}
// Counter component function
async function Counter(
// component properties
{ initialValue }: ComponentProps<CounterProps>,
// component context
{ component }: Contexts
) {
let counter = initialValue || 0;
// Render
component.renderSync(
<>
<Button key="increment-counter-button">Increment</Button>
<div key="counter">{counter}</div>
</>
);
// create AsyncGenerator
const events = component.waitEvents(
// listen "click" event on element tagged by the 'increment-counter-button' key
component.domEvent('increment-counter-button', 'click')
);
// Wait for events
for await (const _ of events) {
counter++;
// Replace a child by key
component.replaceChild('counter', () => <div key="counter">{counter}</div>);
}
}
start(document.getElementById('root')!, Counter, {}, {});
See live example
// Imports
import { Contexts, ComponentProps, start } from '@seqflow/seqflow';
import { Loading } from '@seqflow/components';
import '@seqflow/components/style.css';
// Quote interface
interface Quote {
author: string;
content: string;
}
// Pure function to fetch a random quote
async function getRandomQuote(): Promise<Quote> {
const res = await fetch('https://quotes.seqflow.dev/api/quotes/random');
if (!res.ok) {
throw new Error('Failed to fetch quote');
}
return await res.json();
}
// RandomQuote component function
export async function RandomQuote(
// component properties
{}: ComponentProps<unknown>,
// component context
{ component }: Contexts
) {
// Render
component.renderSync(<Loading />);
// Async invocation inside the component
let quote: Quote;
try {
quote = await getRandomQuote();
} catch (error) {
component.renderSync(<div>Error: {(error as Error).message}</div>);
return;
}
component.renderSync(
<blockquote>
<p>{quote.content}</p>
<footer>{quote.author}</footer>
</blockquote>
);
}
start(document.getElementById('root')!, RandomQuote, {}, {});
See live example