feat(dev-tools): adiciona plugin dev-tools (agents, skills, knowledge e metadados do projeto)

This commit is contained in:
2026-10-28 16:08:45 +00:00
parent ce6030ee00
commit 7b2162ff29
180 changed files with 52164 additions and 0 deletions
+181
View File
@@ -0,0 +1,181 @@
---
name: react-patterns
description: Padroes modernos React (18+/19) -- hooks, context, composicao, memoizacao, lazy loading e Server Components.
layer: wiki
---
# React Patterns
Skill para desenvolvimento React seguindo padroes modernos (React 18+/19).
## Contexto NotebookLM
ANTES de executar, consultar notebooks para contexto especializado:
| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| Programacao | 24947ffa-0019-448a-a340-2f4a275d2eb1 | Sempre |
```
mcp__notebooklm__notebook_query({
notebook_id: "24947ffa-0019-448a-a340-2f4a275d2eb1",
query: "<adaptar ao contexto do pedido do utilizador>"
})
```
> **Regra #48:** Projectos React devem ser desenvolvidos no **container dev** (`server:"dev"`, path `/root/Dev/<projecto>`). Sincroniza via Syncthing.
## Quando Usar
- Desenvolver componentes React
- Implementar state management
- Optimizar performance (memoization, lazy loading)
- Migrar de class components para hooks
- Usar Server Components (Next.js App Router)
## Hooks Fundamentais
```jsx
// useState - estado simples
const [count, setCount] = useState(0)
const [user, setUser] = useState<User | null>(null)
// useEffect - side effects
useEffect(() => {
fetchData()
return () => cleanup()
}, [dependency])
// useCallback - memoizar funcoes
const handleClick = useCallback(() => { doSomething(id) }, [id])
// useMemo - memoizar valores computados
const expensiveValue = useMemo(() => computeExpensive(data), [data])
// useRef - referencias mutaveis
const inputRef = useRef<HTMLInputElement>(null)
```
## Component Patterns
```jsx
// SIM: Functional components
function UserCard({ user, onSelect }: Props) {
return <div onClick={() => onSelect(user.id)}>{user.name}</div>
}
// SIM: Composition over props drilling
function Layout({ children }) {
return <main className="container">{children}</main>
}
// NAO: Class components (legacy)
```
## State Management
```jsx
// Local state: useState (estado de um componente)
// Shared state: Context + useReducer
const AppContext = createContext<AppState | null>(null)
function AppProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
)
}
// Server state: React Query / SWR
const { data, isLoading } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
})
```
**NAO:** Redux para tudo. Estado global para estado local.
## Server Components (Next.js 13+)
```jsx
// Server Component (default) - async, data fetching
async function UserList() {
const users = await db.users.findMany()
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}
// Client Component - interactividade
'use client'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// Padrao: Server parent + Client children
// page.tsx (Server) -> InteractiveSection.tsx ('use client')
```
## Performance
```jsx
// Lazy loading componentes
const HeavyComponent = lazy(() => import('./HeavyComponent'))
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
// React.memo para componentes puros
const MemoizedCard = memo(function Card({ data }) {
return <div>{data.title}</div>
})
// Virtualizacao para listas grandes
import { useVirtualizer } from '@tanstack/react-virtual'
```
## Composition Patterns (Vercel Engineering)
Padroes de composicao para componentes React escalaveis. Ver `references/composition-patterns.md` para codigo detalhado.
**Regras principais:**
| Regra | Prioridade | Resumo |
|-------|-----------|--------|
| Evitar Boolean Props | CRITICAL | Variantes explicitas em vez de booleans |
| Compound Components | HIGH | Context partilhado, composicao explicita |
| State em Providers | HIGH | Boundary do Provider e o que importa |
| Interface Generica | HIGH | state/actions/meta para dependency injection |
| Children > Render Props | MEDIUM | Composicao natural com children |
| React 19 APIs | MEDIUM | `use()` e ref como prop |
## Anti-Patterns
| Anti-Pattern | Problema | Solucao |
|--------------|----------|---------|
| useEffect para tudo | Complexidade | Server Components / React Query |
| Props drilling | Manutencao | Context ou Composition |
| Inline functions em JSX | Re-renders | useCallback |
| State em URL | Perda estado | URL state com hooks |
| Class components | Legacy | Functional + Hooks |
| Index como key | Bugs listas | ID unico |
## Checklist Componente
- [ ] Functional component (nao class)
- [ ] TypeScript types/interfaces
- [ ] Props destructuring com defaults
- [ ] Hooks no topo (nao condicionais)
- [ ] Keys unicos em listas
- [ ] Error boundaries para erros
- [ ] Loading states considerados
## Estrutura Ficheiros
```
components/
ui/ # Componentes base (Button, Input, Card)
features/ # Componentes feature-specific
layouts/ # Layout components
providers/ # Context
@@ -0,0 +1,183 @@
# React - Composition Patterns (Vercel Engineering)
Padroes de composicao para componentes React escalaveis. Fonte: Vercel composition-patterns.
## Regra #1: Evitar Boolean Props (CRITICAL)
```tsx
// NAO: Booleans duplicam estados possiveis
<Composer isThread isEditing={false} showAttachments showFormatting={false} />
// SIM: Variantes explicitas
<ThreadComposer channelId="abc" />
<EditMessageComposer messageId="xyz" />
<ForwardMessageComposer messageId="123" />
```
Cada boolean duplica os estados possiveis. 5 booleans = 32 combinacoes, maioria impossivel.
---
## Regra #2: Compound Components com Context (HIGH)
```tsx
// Compound Components: context partilhado, composicao explicita
const ComposerContext = createContext<ComposerContextValue | null>(null)
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
}
function ComposerInput() {
const { state, actions: { update }, meta } = use(ComposerContext)
return (
<TextInput
ref={meta.inputRef}
value={state.input}
onChangeText={(text) => update(s => ({ ...s, input: text }))}
/>
)
}
// Exportar como compound
const Composer = {
Frame: ComposerFrame,
Input: ComposerInput,
Submit: ComposerSubmit,
Footer: ComposerFooter,
}
// Uso: composicao clara
<Composer.Frame>
<Composer.Input />
<Composer.Footer>
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
```
---
## Regra #3: State em Providers (HIGH)
```tsx
// NAO: Estado preso dentro do componente
function ForwardComposer() {
const [state, setState] = useState(initialState)
return <Composer.Frame>...</Composer.Frame>
}
// SIM: State lifted para Provider
function ForwardProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const submit = useForwardMessage()
const inputRef = useRef(null)
return (
<Composer.Provider
state={state}
actions={{ update: setState, submit }}
meta={{ inputRef }}
>
{children}
</Composer.Provider>
)
}
// Qualquer componente dentro do Provider acede ao state
function ForwardDialog() {
return (
<ForwardProvider>
<Dialog>
<ForwardComposer />
<MessagePreview />
<ForwardButton />
</Dialog>
</ForwardProvider>
)
}
function ForwardButton() {
const { actions } = use(ComposerContext)
return <Button onPress={actions.submit}>Forward</Button>
}
```
**Principio chave:** O boundary do Provider e o que importa, nao o nesting visual.
---
## Regra #4: Interface Generica para Dependency Injection (HIGH)
```tsx
// Definir interface generica: state + actions + meta
interface ComposerContextValue {
state: { input: string; attachments: Attachment[]; isSubmitting: boolean }
actions: { update: (fn: (s: State) => State) => void; submit: () => void }
meta: { inputRef: React.RefObject<TextInput> }
}
// Multiplos providers implementam a MESMA interface
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
return <Composer.Provider state={state} actions={{ update, submit }}>{children}</Composer.Provider>
}
function ForwardProvider({ children }) {
const [state, setState] = useState(initialState)
return <Composer.Provider state={state} actions={{ update: setState, submit: forward }}>{children}</Composer.Provider>
}
// O mesmo Composer.Input funciona com AMBOS os providers
```
**Principio:** UI = pecas reutilizaveis. State = injectado pelo provider. Trocar provider, manter UI.
---
## Regra #5: Children > Render Props (MEDIUM)
```tsx
// NAO: Render props (inflexivel)
<Composer
renderHeader={() => <Header />}
renderFooter={() => <><Formatting /><Emojis /></>}
/>
// SIM: Children (composicao natural)
<Composer.Frame>
<Header />
<Composer.Input />
<Composer.Footer>
<Formatting />
<Emojis />
</Composer.Footer>
</Composer.Frame>
// Excepcao: render props quando parent passa dados
<List data={items} renderItem={({ item }) => <Item item={item} />} />
```
---
## Regra #6: React 19 APIs (MEDIUM)
```tsx
// React 19: ref como prop normal (sem forwardRef)
function Input({ ref, ...props }: Props & { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />
}
// React 19: use() em vez de useContext()
const value = use(MyContext) // pode ser chamado condicionalmente
```
---
## Checklist Composicao
- [ ] Zero boolean props para variantes (usar componentes explicitos)
- [ ] Compound components com context partilhado
- [ ] State em Provider (nao dentro do componente UI)
- [ ] Interface generica (state/actions/meta)
- [ ] Children para composicao, render props so para data passthrough
- [ ] React 19: `use()` e ref como prop
@@ -0,0 +1,66 @@
# React - Custom Hooks Uteis
## useDebounce
```tsx
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Uso
const searchTerm = useDebounce(input, 500);
```
---
## useLocalStorage
```tsx
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
};
return [storedValue, setValue] as const;
}
```
---
## useFetch
```tsx
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
```