Components
Components are the building blocks for creating JSX markup. Each component is a function that implements the PC type (for synchronous components) or APC type (for asynchronous components). The first argument to such a function are the parameters, and the second is the available PreffX utilities.
Implementation features:
- Reactivity: Uses the signal system (
@preact/signals-core) to create and track reactive values. Only such values trigger re-rendering. - Lifecycle management: Runs callbacks on component mount and unmount and automatically removes effects
- Passing PreffX utilities as second argument: Everything needed comes in parameters, only types need to be imported from PreffX itself
- Support for asynchronous functions: Works with both synchronous and asynchronous functions, automatically re-rendering the DOM tree when promises resolve
Basic Implementation
The simplest implementation of a component with a counter looks like this:
import type { PC } from 'preffx';
const MyComponent: PC = (props, { signal, computed }) => {
const { initialCount = 0 } = props;
const count = signal(initialCount);
return (
<div>
<p>Counter: {count}</p>
<button onClick={() => count.value++}>Increase</button>
</div>
);
};
const App: PC = () => {
return <MyComponent
initialCount={5}
/>;
};Reactivity
To ensure reactivity, functions implemented on top of @preact/signals-core are used, for example:
signal- creates a reactive signal for state,computed- computes dependent values,effect- manages side effects.
These and other functions fully implement the behavior described in the Preact Signals documentation.
Component Context
For each component instance, a context is created that extends the parent context. This is a regular object that can be taken from utilities:
import type { PC } from 'preffx';
const contextKey = 'ctx-counter';
const AnotherComponent: PC = (props, { context }) => {
// read context
const counter = context[contextKey];
return <span>
{counter}
</span>;
};
export const App: PC = (props, { signal, context }) => {
const counter = signal(0);
// modify context
context[contextKey] = counter;
return <p>
{valueFromContext}
<AnotherComponent />
</p>;
};Lifecycle
Each component can use lifecycle utilities:
onMount- called when the component is mounted - after its elements are added to the DOM tree;onDestroy- called when the component is destroyed - before its elements are removed from the DOM tree
import type { PC } from 'preffx';
export const App: PC = (props, { onMount, onDestroy }) => {
const { count } = props;
onMount(() => {
// some mount logic
});
onDestroy(() => {
// some destroy logic
});
return <button
onClick={() => {
count.value += 1
}}
>
Count is {count}
</button>
};ID Generation
The id utility allows generating unique identifiers for use inside components:
import type { PC } from 'preffx';
export const App: PC = (props, { id }) => {
// get unique id
const inputId = id();
return <div>
<label>
Password:
<input
type="password"
aria-describedby={inputId}
/>
</label>
<p id={inputId}>
The password should contain at least 18 characters
</p>
</div>;
};Special Components
Among the utilities, special built-in components can be highlighted:
Catch- for error handling in components,For- for rendering lists with reactivity support,Defer- for deferred rendering of values that are computed asynchronously,Portal- for rendering elements outside the PreffX root