Skip to content

Начало работы

PreffX — это самоуверенная JS-библиотека для создания реактивного DOM. Она вдохновлена React и Preact, но предлагает собственный подход на основе сигналов.

⚠️ Проект находится в экспериментальной стадии, не используйте его в продакшене ⚠️

Основные принципы

  • Синтаксис JSX, похожий на React;
  • Preact signals как ядро реактивности;
  • только сигналы вызывают перерисовку;
  • каждый компонент выполняется только один раз;
  • свойства компонентов передаются в качестве первого аргумента, а все утилиты передаются во втором аргументе — нет необходимости импортировать их отдельно,
  • поддерживаются как синхронные, так и асинхронные функциональные компоненты;
  • различие между свойствами и атрибутами (все свойства имеют префикс $) — например, $value — это свойство, а value — это атрибут.

Установка

Попробуйте демо Vite + PreffX

Примеры

  • Простой счетчик:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { signal }) => {
    const count = signal(0);
    return <button
        onClick={() => {
            count.value += 1
        }}
    >
        Count is {count}
    </button>
};
  • Как создать ссылки на элементы:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { signal }) => {
    const signalRef = signal();
    return <div $ref={signalRef}>
        <div
          $ref={(refVal) => {
            // будет вызвано после монтирования элемента с refVal = HTMLDivElement
            // и перед уничтожением элемента с refVal = null
          }}
        >
            Refs
        </button>
    </div>
};
  • Хуки жизненного цикла:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { onMount, onDestroy }) => {
    const { count } = props;

    onMount(() => {
        // логика монтирования
    });
    
    onDestroy(() => {
        // логика уничтожения
    });
    return <button
        onClick={() => {
            count.value += 1
        }}
    >
        Count is {count}
    </button>
};
  • Рендеринг списков:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { For }) => {
    const items = signal([
        {
            name: 'First'
        },
        {
            name: 'Second'
        }
    ]);

    return <ul>
        <For
            items={items}
            callback={(item) => <li>Item with name: {item.name}</li>}
            fallback={<li>No items</li>}
        />
    </ul>;
};
  • Обработка контекста:
tsx
import type { PC } from 'preffx';

const contextKey = 'ctx-counter';

const AnotherComponent: PC = (props, { context }) => {
    // чтение контекста
    const counter = context[contextKey];
    return <span>
        {counter}
    </span>;
};

export const App: PC = (props, { signal, context }) => {
    const counter = signal(0);
    // изменение контекста
    context[contextKey] = counter;
    return <p>
        {valueFromContext}
        <AnotherComponent />
    </p>;
};
  • Асинхронные компоненты:
tsx
import type { APC } from 'preffx';
import { getData } from './data';
import { AnotherComponent, AnotherAsyncComponent } from './components';

const AsyncComponent: APC<{
    name: string;
}> = async (props, utils) => {
    const data = await getData()
    const componentRoot = await <AnotherAsyncComponent name='nested'/>;
    return <div>
        <AnotherComponent data={data} />
        {componentRoot}
    </div>;
}
  • Обработка ошибок:
tsx
import type { PC } from 'preffx';
import { AnotherComponent } from './components';

export const App: PC = (props, { Catch }) => {
    return <div>
        Иногда компоненты возвращают ошибки
        <Catch fallback={<div>Catched!</div>}>
            <AnotherComponent />
        </Catch>
    </div>;
};
  • Обработка отложенных значений:
tsx
import type { PC } from 'preffx';
import { AsyncComponent } from './components';

export const App: PC = (props, { computed, Defer }) => {
    const def = computed(() => <AsyncComponent id={props.id} />);
    return <Defer
        value={def}
        initial={<div>Пожалуйста, подождите</div>}
    />;
};
  • Порталы:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { Portal }) => {
    return <div>
        <span>Некоторый текст внутри текущего дерева</span>
        <Portal root={document.getElementById('portal')} >
            <div>Текст внутри портала</div>
        </Portal>
    </div>;
};
  • Уникальные идентификаторы:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { id }) => {
    // получить уникальный id
    const inputId = id();
    return <div>
        <label>
            Пароль:
            <input
                type="password"
                aria-describedby={inputId}
            />
        </label>
        <p id={inputId}>
            Пароль должен содержать как минимум 18 символов
        </p>
    </div>;
};

Опубликовано под лицензией Apache License 2.0