Начало работы
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>;
};