Ativar quando o usuário mencionar "lógica", "hook", "useState", "useEffect", "efeito colateral", "estado", "componente", "função", ou quando estiver escrevendo código que não seja puramente visual. Use esta skill SEMPRE que adicionar qualquer lógica a componentes para garantir que esteja corretamente isolada em hooks ou services.
Proibido lógica dentro de componentes. Componentes devem ser puramente declarativos. Toda lógica deve existir em hooks ou domain/services.
Lógica inclui qualquer código que:
useState, useReducer)useEffect, useAsync)// ❌ RUIM - Lógica no componente
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
if (users.length === 0) return <EmptyState />;
return (
<ul>
{users.map(user => (
<li key={user.id}>
<UserCard name={user.name} email={user.email} />
</li>
))}
</ul>
);
}
// ✅ BOM - Lógica isolada em hook
function UserList() {
const { users, loading, error, refetch } = useUsers();
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} onRetry={refetch} />;
if (users.length === 0) return <EmptyState />;
return (
<ul>
{users.map(user => (
<li key={user.id}>
<UserCard name={user.name} email={user.email} />
</li>
))}
</ul>
);
}
// hooks/useUsers.ts
export function useUsers() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchUsers = useCallback(async () => {
setLoading(true);
setError(null);
try {
const data = await userService.getAll();
setUsers(data);
} catch (err) {
setError(err instanceof Error ? err : new Error('Erro desconhecido'));
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
return {
users,
loading,
error,
refetch: fetchUsers,
};
}
// hooks/useFetch.ts
export function useFetch<T>(url: string, options?: FetchOptions) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
// ... implementação
return { data, loading, error, refetch };
}
// hooks/useMutation.ts
export function useMutation<TData, TVariables>(
mutationFn: (variables: TVariables) => Promise<TData>
) {
const [state, setState] = useState<MutationState<TData>>({
loading: false,
error: null,
data: null,
});
const mutate = useCallback(async (variables: TVariables) => {
setState(s => ({ ...s, loading: true, error: null }));
try {
const data = await mutationFn(variables);
setState({ loading: false, data, error: null });
return data;
} catch (error) {
setState({ loading: false, data: null, error });
throw error;
}
}, [mutationFn]);
return { ...state, mutate };
}
// hooks/useForm.ts
export function useForm<T>(initialValues: T, validationSchema?: Schema) {
const [values, setValues] = useState<T>(initialValues);
const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});
const [touched, setTouched] = useState<Partial<Record<keyof T, boolean>>>({});
const handleChange = useCallback((field: keyof T, value: T[keyof T]) => {
setValues(prev => ({ ...prev, [field]: value }));
}, []);
const validate = useCallback(async () => {
if (!validationSchema) return true;
const result = await validationSchema.validate(values);
setErrors(result.errors);
return result.isValid;
}, [values, validationSchema]);
// ...
return { values, errors, touched, handleChange, validate };
}
src/
├── components/
│ └── UserList/
│ └── UserList.tsx # Apenas UI
├── hooks/
│ └── useUsers.ts # Lógica de estado e fetch
├── services/
│ └── userService.ts # Comunicação com API
└── domain/
└── validators/
└── userValidator.ts # Validações de negócio
useState, useEffect dentro de componentes de UI