Skip to content

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:

  1. Reactivity: Uses the signal system (@preact/signals-core) to create and track reactive values. Only such values trigger re-rendering.
  2. Lifecycle management: Runs callbacks on component mount and unmount and automatically removes effects
  3. Passing PreffX utilities as second argument: Everything needed comes in parameters, only types need to be imported from PreffX itself
  4. 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:

typescript
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:

tsx
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
tsx
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:

tsx
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

Released under the Apache-2.0 License.