🎯

react-effect-patterns

🎯Skill

from nbbaier/agent-skills

VibeIndex|
What it does

react-effect-patterns skill from nbbaier/agent-skills

react-effect-patterns

Installation

Quick InstallInstall with npx
npx skills add nbbaier/agent-skills
πŸ“– Extracted from docs: nbbaier/agent-skills
8
-
Last UpdatedJan 26, 2026

Skill Details

SKILL.md

Guidelines for proper React useEffect usage and avoiding unnecessary Effects. Use when writing, reviewing, or refactoring React components that use useEffect, useState, or handle side effects. Triggers on tasks involving React Effects, derived state, event handlers, data fetching, or component synchronization.

Overview

# React Effect Patterns

Effects are an escape hatch from React for synchronizing with external systems. Removing unnecessary Effects makes code easier to follow, faster to run, and less error-prone.

When to Use Effects

  • Synchronizing with external systems (non-React widgets, network, browser DOM)
  • Analytics on component display
  • Data fetching (with cleanup for race conditions)

When NOT to Use Effects

  • Transforming data for rendering
  • Handling user events
  • Updating state based on other state

Decision Tree

Why does this code run?

  • Component displayed β†’ Effect
  • User action β†’ Event handler
  • Calculable from props/state β†’ Calculate during render

---

Anti-Patterns and Solutions

1. Derived State

```jsx

// ❌ Bad

const [fullName, setFullName] = useState("");

useEffect(() => {

setFullName(firstName + " " + lastName);

}, [firstName, lastName]);

// βœ… Good - calculate during render

const fullName = firstName + " " + lastName;

```

2. Expensive Calculations

```jsx

// ❌ Bad

const [visibleTodos, setVisibleTodos] = useState([]);

useEffect(() => {

setVisibleTodos(getFilteredTodos(todos, filter));

}, [todos, filter]);

// βœ… Good - useMemo for expensive operations

const visibleTodos = useMemo(

() => getFilteredTodos(todos, filter),

[todos, filter],

);

```

Use console.time()/console.timeEnd() to measure. Memoize if β‰₯1ms.

3. Reset State on Prop Change

```jsx

// ❌ Bad

useEffect(() => {

setComment("");

}, [userId]);

// βœ… Good - use key to reset

;

```

4. Adjust Part of State on Prop Change

```jsx

// ❌ Bad

useEffect(() => {

setSelection(null);

}, [items]);

// βœ… Good - derive from state

const selection = items.find((item) => item.id === selectedId) ?? null;

```

5. Event-Specific Logic

```jsx

// ❌ Bad

useEffect(() => {

if (product.isInCart) {

showNotification("Added " + product.name + "!");

}

}, [product]);

// βœ… Good - in event handler

function handleBuyClick() {

addToCart(product);

showNotification("Added " + product.name + "!");

}

```

6. Form Submission

```jsx

// ❌ Bad

useEffect(() => {

if (jsonToSubmit !== null) {

post("/api/register", jsonToSubmit);

}

}, [jsonToSubmit]);

// βœ… Good - in event handler

function handleSubmit(e) {

e.preventDefault();

post("/api/register", { firstName, lastName });

}

```

7. Effect Chains

```jsx

// ❌ Bad - cascading Effects

useEffect(() => {

setGoldCardCount((c) => c + 1);

}, [card]);

useEffect(() => {

setRound((r) => r + 1);

}, [goldCardCount]);

useEffect(() => {

setIsGameOver(true);

}, [round]);

// βœ… Good - calculate + update in handler

const isGameOver = round > 5;

function handlePlaceCard(nextCard) {

setCard(nextCard);

if (nextCard.gold) {

if (goldCardCount < 3) {

setGoldCardCount(goldCardCount + 1);

} else {

setGoldCardCount(0);

setRound(round + 1);

}

}

}

```

8. App Initialization

```jsx

// ❌ Bad - runs twice in dev

useEffect(() => {

loadDataFromLocalStorage();

checkAuthToken();

}, []);

// βœ… Good - module level or guard

let didInit = false;

function App() {

useEffect(() => {

if (!didInit) {

didInit = true;

loadDataFromLocalStorage();

checkAuthToken();

}

}, []);

}

```

9. Notify Parent of State Changes

```jsx

// ❌ Bad - extra render pass

useEffect(() => {

onChange(isOn);

}, [isOn, onChange]);

// βœ… Good - update both in handler

function updateToggle(nextIsOn) {

setIsOn(nextIsOn);

onChange(nextIsOn);

}

// βœ… Also good - lift state (controlled component)

function Toggle({ isOn, onChange }) {

function handleClick() {

onChange(!isOn);

}

}

```

10. Pass Data to Parent

```jsx

// ❌ Bad - child fetches, passes up

useEffect(() => {

if (data) onFetched(data);

}, [data]);

// βœ… Good - parent fetches, passes down

function Parent() {

const data = useSomeAPI();

return ;

}

```

11. External Store Subscription

```jsx

// ❌ Bad - manual subscription

useEffect(() => {

const handler = () => setIsOnline(navigator.onLine);

window.addEventListener("online", handler);

window.addEventListener("offline", handler);

return () => {

window.removeEventListener("online", handler);

window.removeEventListener("offline", handler);

};

}, []);

// βœ… Good - useSyncExternalStore

return useSyncExternalStore(

subscribe,

() => navigator.onLine,

() => true,

);

```

12. Data Fetching with Race Conditions

```jsx

// βœ… Correct - cleanup ignores stale responses

useEffect(() => {

let ignore = false;

fetchResults(query).then((json) => {

if (!ignore) setResults(json);

});

return () => {

ignore = true;

};

}, [query]);

```

---

Quick Reference

| Scenario | Solution |

| ----------------------- | ------------------------------------ |

| Transform data | Calculate during render |

| Expensive calculation | useMemo |

| Reset all state on prop | key attribute |

| Adjust state on prop | Derive during render |

| Share event logic | Extract function, call from handlers |

| User events | Event handlers |

| External system sync | Effect |

| Notify parent | Update in handler or lift state |

| Init once | Module-level or guard variable |

| External store | useSyncExternalStore |

| Fetch data | Effect with cleanup |