feat: refactor 30+ skills to Anthropic progressive disclosure pattern
- All SKILL.md files now <500 lines (avg reduction 69%) - Detailed content extracted to references/ subdirectories - Frontmatter standardised: only name + description (Anthropic standard) - New skills: brand-guidelines, spec-coauthor, report-templates, skill-creator - Design skills: anti-slop guidelines, premium-proposals reference - Removed non-standard frontmatter fields (triggers, version, author, category) Plugins affected: infraestrutura, marketing, dev-tools, crm-ops, gestao, core-tools, negocio, perfex-dev, wordpress, design-media Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
66
dev-tools/skills/react-patterns/references/custom-hooks.md
Normal file
66
dev-tools/skills/react-patterns/references/custom-hooks.md
Normal file
@@ -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 };
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user