Loading...
Loading...
React patterns, anti-patterns, and performance optimization. Use when writing React components, reviewing React code, or debugging React issues.
npx skill4agent add amorriscode/agent-grimoire react-development// ❌ Mutates external variable
let guest = 0;
function Cup() {
guest = guest + 1;
return <h2>Guest #{guest}</h2>;
}
// ✅ Uses props
function Cup({ guest }) {
return <h2>Guest #{guest}</h2>;
}// ❌ Redundant state
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState(''); // redundant!
// ✅ Compute during render
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;// Forces fresh component instance when recipient changes
<Chat key={recipientId} contact={recipient} />| Scenario | Instead of Effect | Do This |
|---|---|---|
| Transform data for render | | Calculate during render |
| Cache expensive calculations | | |
| Reset state on prop change | | Use |
| Handle user events | | Event handler directly |
| Notify parent of changes | | Call in event handler |
useEffect(() => {
const connection = createConnection();
connection.connect();
return () => connection.disconnect(); // cleanup
}, []);childrenimport { FixedSizeList } from 'react-window';
<FixedSizeList height={400} itemCount={1000} itemSize={35}>
{({ index, style }) => <div style={style}>{items[index]}</div>}
</FixedSizeList>const Dashboard = lazy(() => import('./Dashboard'));
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>children// ❌ Prop drilling
<Layout posts={posts} />
// ✅ Composition
<Layout>
<Posts posts={posts} />
</Layout>| Anti-Pattern | Problem | Fix |
|---|---|---|
| Copying props to state | Stale data | Read props directly |
| Effect chains | Cascading renders | Compute in event handlers |
| Suppressing effect with refs | Hides bugs | Add proper cleanup |
| Derived state in useState | Sync issues | Compute during render |
| Missing keys in lists | Broken updates | Use stable unique IDs |
| Index as key (for reorderable lists) | State mismatch | Use item IDs |
function ProductList({ products, onSelect }) {
// Derived state — computed, not stored
const sortedProducts = useMemo(
() => [...products].sort((a, b) => a.name.localeCompare(b.name)),
[products]
);
// Event handler — not useEffect
function handleClick(product) {
onSelect(product);
}
return (
<ul>
{sortedProducts.map(product => (
<li key={product.id} onClick={() => handleClick(product)}>
{product.name}
</li>
))}
</ul>
);
}function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
const data = await getUser(userId);
if (!cancelled) setUser(data);
}
fetchUser();
return () => { cancelled = true; };
}, [userId]);
if (!user) return <Loading />;
return <Profile user={user} />;
}