Skip to content

Empezando

PreffX es una biblioteca JavaScript independiente para crear un DOM reactivo. Se inspira en React y Preact, pero ofrece su propio enfoque basado en señales.

⚠️ El proyecto se encuentra actualmente en fase experimental; no lo utilice en un entorno de producción. ⚠️

Principios básicos

  • Sintaxis JSX similar a React;
  • Señales de Preact como núcleo de reactividad;
  • Solo las señales provocan un nuevo renderizado;
  • Cada componente se ejecuta solo una vez;
  • Las props del componente se reciben como primer argumento y todas las utilidades como segundo argumento; no es necesario importarlas;
  • Compatibilidad con componentes funcionales síncronos y asíncronos;
  • Distinción entre propiedades y atributos (todas las propiedades tienen el prefijo $); por ejemplo, $value es una propiedad y value es un atributo.

Instalación

La forma recomendada es usar la utilidad interactiva create-preffx, que permite definir el nombre, el idioma y la solución CSS del proyecto:

bash
npx create-preffx

Alternativamente, puedes usar degit:

bash
npx degit msabitov/vite-preffx preffx-starter
cd preffx-starter
npm install
npm run dev

También puedes probar la demo de StackBlitz

Ejemplos

  • Contador simple:
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>
};
  • Cómo crear referencias a elementos:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { signal }) => {
    const signalRef = signal();
    return <div $ref={signalRef}>
        <div
            $ref={(refVal) => {
                // it will be called after element mounted with refVal = HTMLDivElement
                // and before element destroyed with refVal = null
            }}
            $onMount={(refVal) => {
                // it will be called after element mounted with refVal = HTMLDivElement
            }}
            $onDestroy={() => {
                // it will be called before element destroyed with refVal = HTMLDivElement
            }}
        >
            Refs
        </button>
    </div>
};
  • Ganchos del ciclo de vida:
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>
};
  • Representación de la lista:
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>;
};
  • Manejo del contexto:
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>;
};
  • Componentes asíncronos:
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>;
}
  • Manejo de errores:
tsx
import type { PC } from 'preffx';
import { AnotherComponent } from './components';

export const App: PC = (props, { Catch }) => {
    return <div>
        Sometimes components return errors
        <Catch fallback={<div>Catched!</div>}>
            <AnotherComponent />
        </Catch>
    </div>;
};
  • Gestión de valores diferidos:
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>Please wait</div>}
    />;
};
  • Portal:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { Portal }) => {
    return <>
        <span>Some text inside current tree</span>
        <Portal root={document.getElementById('portal')}>
            <div>Text inside portal</div>
        </Portal>
    </>;
};
  • Identificadores únicos:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { id }) => {
    // get unique id
    const inputId = id();
    return <>
        <label>
            Password:
            <input
                type="password"
                aria-describedby={inputId}
            />
        </label>
        <p id={inputId}>
            The password should contain at least 18 characters
        </p>
    </>;
};
  • Enrutamiento simple:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { computed, url }) => {
    const routeContent = computed(() => {
        // depends on url signal
        switch(url.value.pathname) {
            case '/home':
                return <div>Home page content</div>
            case '/contacts':
                return <div>Contacts page content</div>;
            default:
                return <div>Other page content</div>
        }
    });
    return <div>
        <a href='/home'>Home</a>
        <a href='/contacts'>Contacts</a>
        {routeContent}
    </div>;
};
  • Ejemplo de i18n:
tsx
import type { PC } from 'preffx';

const dictionary = {
    en: {
        openProfile: 'Open profile',
        showStats: 'Show statistics'
    },
    ru: {
        openProfile: 'Открыть профиль',
        showStats: 'Показать статистику'
    }
};

export const App: PC = (props, { computed, lang }) => {
    const captions = computed(() => {
        // depends on lang signal
        return dictionary[lang.value] || dictionary.en;
    });
    return <div>
        <button>{captions.openProfile}</button>
        <button>{captions.showStats}</button>
    </div>;
};

Publicado bajo la Licencia Apache 2.0