games:
  - type: bug-hunt
    title: "React Anti-Pattern Detective"
    snippets:
      - code: |
          function Counter() {
            const [count, setCount] = useState(0);
            return (
              <button onClick={() => count++}>Count: {count}</button>
            );
          }
        bugLine: 4
        explanation: "Mutating state directly — count++ mutates state in place. React won't re-render. Use setCount(count + 1) instead."
        hint: "Does count++ actually trigger a re-render?"
      - code: |
          function TodoList({ items }) {
            return (
              <ul>
                {items.map((item) => (
                  <li>{item.text}</li>
                ))}
              </ul>
            );
          }
        bugLine: 5
        explanation: "Missing key prop — React needs a unique key for each list item to track identity and avoid rendering bugs. Use key={item.id} or a stable identifier."
        hint: "What does React need to efficiently reconcile list items?"
      - code: |
          function DataFetcher() {
            const [data, setData] = useState(null);
            useEffect(() => {
              fetch('/api/data')
                .then((res) => res.json())
                .then(setData);
            }, [data]);
            return <div>{data ? data.name : 'Loading...'}</div>;
          }
        bugLine: 6
        explanation: "Infinite useEffect loop — the dependency array includes [data], so every time setData runs, the effect re-fires. Use [] for one-time fetch."
        hint: "What happens when setData runs? Does the effect run again?"
      - code: |
          function SearchBar() {
            const [query, setQuery] = useState('');
            const handleSearch = () => {
              setTimeout(() => {
                fetchResults(query);
              }, 500);
            };
            return <input onChange={(e) => setQuery(e.target.value)} />;
          }
        bugLine: 5
        explanation: "Stale closure — the setTimeout callback captures query at callback creation. If the user types during the 500ms delay, fetchResults gets the old query. Use a ref or pass query explicitly."
        hint: "What value of query does the setTimeout callback see when it runs?"
      - code: |
          function Parent() {
            const [count, setCount] = useState(0);
            const handleClick = () => setCount(count + 1);
            return <Child onClick={handleClick} />;
          }
          function Child({ onClick }) {
            return <button onClick={onClick}>Increment</button>;
          }
        bugLine: 3
        explanation: "Recreated callback on every render — handleClick is a new function each render, causing Child to re-render unnecessarily. Wrap in useCallback for stable reference."
        hint: "Is handleClick stable across Parent re-renders?"
      - code: |
          function ExpensiveList({ items }) {
            const sorted = items.sort((a, b) => a.name.localeCompare(b.name));
            return (
              <ul>
                {sorted.map((item) => <li key={item.id}>{item.name}</li>)}
              </ul>
            );
          }
        bugLine: 3
        explanation: "Sort mutates and runs every render — .sort() mutates the original array (can cause bugs) and recomputes on every render. Use useMemo with a copy: [...items].sort(...)."
        hint: "Does sort mutate? Does this run on every render?"

  - type: speed-round
    title: "Hook Quick Fire"
    rounds:
      - question: "You need to store a number that changes when the user clicks a button. Which hook?"
        options:
          - "useEffect"
          - "useState"
          - "useRef"
          - "useMemo"
        answer: 1
        timeLimit: 14
      - question: "You need to fetch data when the component mounts. Which hook?"
        options:
          - "useState"
          - "useEffect"
          - "useCallback"
          - "useRef"
        answer: 1
        timeLimit: 14
      - question: "You need a mutable value that persists across renders but doesn't trigger re-renders when it changes. Which hook?"
        options:
          - "useState"
          - "useEffect"
          - "useRef"
          - "useMemo"
        answer: 2
        timeLimit: 16
      - question: "You're computing an expensive derived value from props. You want to avoid recomputing when other state changes. Which hook?"
        options:
          - "useCallback"
          - "useEffect"
          - "useMemo"
          - "useRef"
        answer: 2
        timeLimit: 16
      - question: "You're passing a callback to a memoized child; the callback depends on parent state. Which hook keeps the callback stable?"
        options:
          - "useMemo"
          - "useEffect"
          - "useCallback"
          - "useRef"
        answer: 2
        timeLimit: 16
      - question: "You need to share data (e.g. theme) with many components without prop drilling. Which hook?"
        options:
          - "useState"
          - "useContext"
          - "useMemo"
          - "useRef"
        answer: 1
        timeLimit: 14
      - question: "You need to store a DOM reference so you can call .focus() or .scrollIntoView(). Which hook?"
        options:
          - "useState"
          - "useEffect"
          - "useRef"
          - "useContext"
        answer: 2
        timeLimit: 14
      - question: "You need to run a side effect (e.g. subscribe to an event) when the component mounts and clean up when it unmounts. Which hook?"
        options:
          - "useState"
          - "useEffect"
          - "useCallback"
          - "useMemo"
        answer: 1
        timeLimit: 14
