react
<core_principles>
Core Principles
1. Minimize useEffect Usage
Think harder before adding useEffect. Most scenarios have better alternatives:
<when_not_to_use_effect>
When NOT to Use useEffect
Data transformation for rendering:
- ❌ Don't: Use Effect to compute derived state
- ✅ Do: Calculate during render at top level
// ❌ Bad: Cascading updates
const [filteredItems, setFilteredItems] = useState<Item[]>([]);
useEffect(() => {
setFilteredItems(items.filter(item => item.active));
}, [items]);
// ✅ Good: Direct computation
const filteredItems = items.filter(item => item.active);
Handling user events:
- ❌ Don't: Use Effect to respond to user actions
- ✅ Do: Handle logic in event handlers
// ❌ Bad: Lost interaction context
useEffect(() => {
if (buttonClicked) {
submitForm();
}
}, [buttonClicked]);
// ✅ Good: Explicit intent
const handleSubmit = () => {
submitForm();
};
Caching expensive computations:
- ❌ Don't: Store computed results in state via Effects
- ✅ Do: Use useMemo
// ❌ Bad: Manual memoization
const [expensiveResult, setExpensiveResult] = useState<Result | null>(null);
useEffect(() => {
setExpensiveResult(computeExpensiveValue(data));
}, [data]);
// ✅ Good: useMemo
const expensiveResult = useMemo(() => computeExpensiveValue(data), [data]);
Resetting state on prop changes:
- ❌ Don't: Use Effect to reset state
- ✅ Do: Use key prop to force remount
// ❌ Bad: Manual synchronization
useEffect(() => {
setLocalState(defaultValue);
}, [userId]);
// ✅ Good: Key-based reset
<Profile key={userId} userId={userId} />
</when_not_to_use_effect>
<legitimate_use_cases>
Legitimate useEffect Use Cases
Only use Effects for:
- Synchronizing with external systems (browser APIs, third-party widgets)
- Data fetching with proper cleanup (avoid race conditions)
- Subscriptions (prefer useSyncExternalStore when possible)
Pattern for data fetching (if not using query client):
useEffect(() => {
let ignore = false;
async function fetchData() {
const result = await api.getData();
if (!ignore) {
setData(result);
}
}
fetchData();
return () => { ignore = true; }; // Cleanup to prevent race conditions
}, [dependency]);
</legitimate_use_cases> </core_principles>
<component_definition>
2. Component Definition Style
Always use FC type annotation:
import { FC, PropsWithChildren } from 'react';
// For components without children
type ButtonProps {
label: string;
onClick: () => void;
}
const Button: FC<ButtonProps> = ({ label, onClick }) => {
return <button onClick={onClick}>{label}</button>;
};
// For components that accept children
type CardProps {
title: string;
}
const Card: FC<PropsWithChildren<CardProps>> = ({ title, children }) => {
return (
<div>
<h2>{title}</h2>
<div>{children}</div>
</div>
);
};
Never use function declaration syntax:
// ❌ Bad: Avoid this style
function MyComponent(props: Props) {
return <div />;
}
</component_definition>
<api_requests>
3. API Request Handling
Never use fetch directly in components. Always use the project's query client.
<detection_workflow>
Detection Workflow
-
Check project dependencies in package.json:
@apollo/client→ Use Apollo Client hooks@tanstack/react-query→ Use Tanstack Query hooksswr→ Use SWR hooks
-
Search for existing usage patterns:
- Look for
useQuery,useMutation,useSWR,useApolloClientin codebase - Follow established patterns for consistency
- Look for
-
Apply appropriate client:
<apollo_client> Apollo Client (GraphQL):
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
const UserProfile: FC<{ userId: string }> = ({ userId }) => {
const { data, loading, error } = useQuery(GET_USER, {
variables: { id: userId },
});
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <div>{data.user.name}</div>;
};
// Mutations
const UPDATE_USER = gql`
mutation UpdateUser($id: ID!, $name: String!) {
updateUser(id: $id, name: $name) {
id
name
}
}
`;
const EditForm: FC = () => {
const [updateUser, { loading }] = useMutation(UPDATE_USER);
const handleSubmit = async (values: FormValues) => {
await updateUser({ variables: { id: values.id, name: values.name } });
};
return <form onSubmit={handleSubmit}>...</form>;
};
</apollo_client>
<tanstack_query> Tanstack Query (REST):
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
const UserProfile: FC<{ userId: string }> = ({ userId }) => {
const { data, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => api.getUser(userId),
});
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <div>{data.name}</div>;
};
// Mutations with cache invalidation
const EditForm: FC = () => {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (values: FormValues) => api.updateUser(values),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['user'] });
},
});
const handleSubmit = (values: FormValues) => {
mutation.mutate(values);
};
return <form onSubmit={handleSubmit}>...</form>;
};
</tanstack_query>
const UserProfile: FC<{ userId: string }> = ({ userId }) => {
const { data, error, isLoading } = useSWR(
/api/users/${userId},
fetcher
);
if (isLoading) return ; if (error) return ;
return {data.name}; };
// Mutations const EditForm: FC = () => { const { trigger, isMutating } = useSWRMutation( '/api/users', updateUser );
const handleSubmit = async (values: FormValues) => { await trigger(values); };
return ...; };
</swr>
</detection_workflow>
**Benefits of query clients**:
- Automatic caching and deduplication
- Loading/error state management
- Race condition handling
- Cache invalidation and refetching
- Optimistic updates support
</api_requests>
</core_principles>
<workflow>
## Implementation Workflow
1. **Before writing component**:
- Identify data dependencies and state requirements
- Think harder: Can state be derived instead of stored?
- Plan event handlers before considering Effects
2. **During implementation**:
- Define component with FC type annotation
- Calculate derived values at top level
- Use useMemo only for expensive computations
- Handle user interactions in event handlers
- Use query client hooks for API requests
3. **Effect review checklist**:
- [ ] Is this synchronizing with an external system?
- [ ] Could this be a calculated value instead?
- [ ] Should this be in an event handler?
- [ ] Am I using the right hook (useMemo, key prop)?
- [ ] If data fetching, is query client available?
4. **If Effect is necessary**:
- Document why Effect is required
- Implement proper cleanup to prevent memory leaks
- Handle race conditions for async operations
</workflow>
<anti_patterns>
## Anti-Patterns to Avoid
❌ **Chaining Effects**:
```tsx
// Bad: Effects triggering each other
useEffect(() => setB(a), [a]);
useEffect(() => setC(b), [b]);
// Good: Direct computation or single event handler
const b = computeB(a);
const c = computeC(b);
❌ Effect-based initialization:
// Bad: One-time initialization in Effect
useEffect(() => {
setData(expensiveInit());
}, []);
// Good: useState with initializer
const [data] = useState(() => expensiveInit());
❌ Direct fetch calls:
// Bad: Manual fetch in component
useEffect(() => {
fetch('/api/data').then(res => res.json()).then(setData);
}, []);
// Good: Use query client
const { data } = useQuery({ queryKey: ['data'], queryFn: fetchData });
</anti_patterns>
More from d-kimuson/dotfiles
article-writing
箇条書きコンテンツを技術記事に仕上げる際に使用する。自然な文体とスタイルで執筆するためのガイドライン。
18n8n
Core n8n automation software using n8n-MCP tools.
17typescript
Must always be enabled when writing/reviewing TypeScript code.
16shadcn-ui
Must always be enabled when working with shadcn-ui.
14frontend-design
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
13skill-installer
Install Codex skills into $CODEX_HOME/skills from a curated list or a GitHub repo path. Use when a user asks to list installable skills, install a curated skill, or install a skill from another repo (including private repos).
13