Skip to content

Signal Conversion

When building user inputs, signals are the best way to pass data in and out of your components.

This example shows two functions trim and debounce that can be combined to change the behavior of inputs.

import { Signal, sig, teardown, watchUpdates } from "rvx";

function trim(source: Signal<string>) {
    const input = sig(source.value);

    // Trim and write into the source signal:
    watchUpdates(input, value => {
        source.value = value.trim();
    });

    // Write into the input when the trimmed version is different:
    watchUpdates(source, value => {
        if (input.value.trim() !== value) {
            input.value = value;
        }
    });

    return input;
}

function debounce<T>(source: Signal<T>, delay: number) {
    const input = sig<T>(source.value);

    let timer: number | undefined;

    // Schedule writing into the source signal:
    watchUpdates(input, value => {
        clearTimeout(timer);
        if (source.value !== value) {
            timer = setTimeout(() => {
                source.value = value;
            }, delay);
        }
    });

    // Always write into the input signal:
    watchUpdates(source, value => {
        input.value = value;
    });

    teardown(() => {
        clearTimeout(timer);
    });

    return input;
}

export function Example() {
    const text = sig("Hello World!");
    return <div class="column">
        <div class="row">
            Trim: <TextInput value={text.pipe(trim)} />
        </div>
        <div class="row">
            Trim & debounce: <TextInput value={text.pipe(trim).pipe(debounce, 500)} />
        </div>
        <div>
            You typed: <b>{() => JSON.stringify(text.value)}</b>
        </div>
    </div>;
}

function TextInput(props: {
    value: Signal<string>;
}) {
    return <input
        type="text"
        prop:value={props.value}
        on:input={event => {
            props.value.value = (event.target as HTMLInputElement).value;
        }}
    />;
}