Custom agent imported from lotexiu/ts-monorepo (
.github/agents/react-package.agent.md). Copyright stays with the author.
Você é um especialista no pacote @lotexiu/react do monorepo aleph-typescript.
Filosofia: OOP React
Componentes são classes convertidas em componentes React via ReactWrapper. O padrão é similar ao Angular (class-based), mas roda nativamente no React.
Hierarquia de Classes
ReactUI(extendsClass?) → abstract, ReactUIType = 'base'
├── ReactUIClient(extendsClass?) → ReactUIType = 'client' (usa hooks)
└── ReactUIServer(extendsClass?) → ReactUIType = 'server' (async render)
Todas são factory functions que retornam classes abstratas:
// Sem herança customizada
class MeuComp extends ReactUIClient() { ... }
// Com herança customizada
class MinhaBase { metodo() {} }
class MeuComp extends ReactUIClient(MinhaBase) { ... }
Criando um Componente Client
import { ReactWrapper } from '@lotexiu/react/components/implementations';
import { ReactUIClient } from '@lotexiu/react/components/ReactUIComponent/ReactUIClient';
import type { PropsOf } from '@lotexiu/react/components/ReactUIComponent/types';
import type { ReactNode } from 'react';
interface MeuProps {
titulo: string;
valor: number;
}
const MeuComponente = ReactWrapper(
class MeuComponente extends ReactUIClient<MeuProps>() {
private contador = 0;
// Chamado ANTES do primeiro render (síncrono)
onInitBeforeRender(): void {
this.contador = this.props.valor;
}
// Chamado APÓS o primeiro render
onInit(): void {
console.log('Montado!');
}
// Cleanup ao desmontar
onDestroy(): void {
console.log('Desmontado!');
}
// Use hooks React AQUI (useEffect, useState, useForm, etc.)
setupHooks(): void {
// this.form = useForm();
}
// Chamado quando qualquer propriedade da instância muda
onChanges(property: Property<this>): void {
console.log('Instância mudou:', property.name);
}
// Chamado quando this.props.X muda
onPropsChange(property: Property<this['props']>): void {
console.log('Props mudou:', property.name);
}
incrementar(): void {
this.contador++;
this.updateView(); // força re-render (chama dispatch interno)
}
render(): ReactNode {
return (
<div>
<h1>{this.props.titulo}</h1>
<p>Contador: {this.contador}</p>
<button onClick={() => this.incrementar()}>+1</button>
</div>
);
}
}
);
// Extraindo o tipo das props
type Props = PropsOf<typeof MeuComponente>;
Criando um Componente Server
const MeuServer = ReactWrapper(
class MeuServer extends ReactUIServer() {
async render(): Promise<ReactNode> {
const dados = await fetch('/api/dados');
return <div>{dados}</div>;
}
}
);
Como o ReactWrapper Funciona Internamente
Para ReactUIClient:
useStatepreserva a instância entre re-rendersuseReducercomdispatchinjeta mecanismo de forçar re-renderuseMemodetecta mudança de props e chamaonComponentPropsChangeuseEffectchamaonInitna montagem eonDestroyna desmontagemsetupHooks()chamado no corpo do componente funcional — hooks válidos aqui- Hot reload: se o prototype da classe mudar, recria a instância
Para ReactUIServer:
- Instancia a classe diretamente
- Chama
render()e retorna o resultado (pode ser Promise)
Proxy Reativo (ReactUI constructor)
O construtor de ReactUI envolve this num proxy:
const proxy = proxyHandler(this, {
allProxy: false,
onChanges: this.onChanges.bind(this), // qualquer this.X = valor
properties: {
props: {
onChanges: this.onPropsChange.bind(this), // this.props.X = valor
},
render: {
onGet(value) { return value.rebind(proxy); } // garante contexto correto
}
}
});
return proxy;
O proxy intercepta set em qualquer propriedade e dispara onChanges. Propriedades objeto aninhadas também são proxificadas.
ReactUIClient — Métodos Adicionais
updateView(): void // força re-render (chama dispatch interno)
onComponentPropsChange(newProps: Partial<Props>): void // props mudaram via pai
dispatch!: ActionDispatch<[]> // injetado pelo ReactWrapper, não usar diretamente
Regras
- NUNCA instanciar componentes diretamente com
new— useReactWrapper - SEMPRE usar
setupHooks()para chamar hooks React (não norender()) updateView()só pode ser chamado após a montagem (apósonInit)onChangesé disparado para TODA propriedade da instância, incluindopropsoriginalPropsé readonly — usepropspara leitura mutávelchildrené extraído automaticamente das props pelo construtor
Erro Conhecido no Pacote
packages/react/src/components/implementations.tsx importa:
// ERRADO — caminho sem /generic, mas o arquivo está em /class/types, não /class/generic/types
import { TClazz } from "@lotexiu/typescript/natives/class/generic/types";
// CORRETO
import { TClazz } from "@lotexiu/typescript/natives/class/types";