Bird
Raised Fist0
NextJSframework~8 mins

State and hooks in client components in NextJS - Performance & Optimization

Choose your learning style10 modes available
Performance: State and hooks in client components
MEDIUM IMPACT
This affects interaction responsiveness and rendering speed of client components by managing state changes efficiently.
Updating state on every keystroke in a text input
NextJS
"use client";
import { useState, useRef } from 'react';

export default function Search() {
  const [query, setQuery] = useState('');
  const timeout = useRef(null);

  function handleChange(e) {
    clearTimeout(timeout.current);
    const value = e.target.value;
    timeout.current = setTimeout(() => setQuery(value), 300);
  }

  return <input defaultValue={query} onChange={handleChange} />;
}
Debounces state updates to reduce re-renders, improving input responsiveness and reducing CPU work.
📈 Performance GainReduces re-renders from every keystroke to once per 300ms pause, lowering INP delay.
Updating state on every keystroke in a text input
NextJS
"use client";
import { useState } from 'react';

export default function Search() {
  const [query, setQuery] = useState('');
  return <input value={query} onChange={e => setQuery(e.target.value)} />;
}
Updates state on every keystroke causing a re-render each time, which can slow down input responsiveness especially with complex components.
📉 Performance CostTriggers 1 re-render per keystroke, increasing INP delay on slow devices.
Performance Comparison
PatternDOM OperationsReflowsPaint CostVerdict
State update on every keystrokeMany small DOM updatesTriggers 1 reflow per updateHigh paint cost due to frequent changes[X] Bad
Debounced state updateFewer DOM updatesSingle reflow after debounceLower paint cost[OK] Good
Multiple independent state variablesMultiple updates possibleMultiple reflows if updated separatelyModerate paint cost[!] OK
Combined state objectSingle update per changeSingle reflowLower paint cost[OK] Good
Rendering Pipeline
State changes trigger React to schedule a re-render of the component. React then recalculates the virtual DOM, diffs it, and updates the real DOM. This affects the Layout and Paint stages in the browser.
→JavaScript Execution
→Style Calculation
→Layout
→Paint
→Composite
⚠️ BottleneckLayout and Paint stages become expensive if many re-renders happen rapidly.
Core Web Vital Affected
INP
This affects interaction responsiveness and rendering speed of client components by managing state changes efficiently.
Optimization Tips
1Avoid updating state on every single user input without throttling or debouncing.
2Group related state variables into a single state object to batch updates.
3Use React hooks wisely to minimize unnecessary re-renders and layout recalculations.
Performance Quiz - 3 Questions
Test your performance knowledge
What is the main performance issue when updating state on every keystroke in a client component?
AIt increases the initial page load time.
BIt causes the browser to download more JavaScript files.
CIt causes a re-render for every keystroke, slowing input responsiveness.
DIt prevents the component from rendering.
DevTools: Performance
How to check: Open DevTools > Performance tab > Record while interacting with the component (e.g., typing). Stop recording and inspect the flame chart for frequent React renders and layout events.
What to look for: Look for many small re-render events and layout recalculations clustered during input, indicating inefficient state updates.

Practice

(1/5)
1. What is the main purpose of using useState in a Next.js client component?
easy
A. To fetch data from the server before rendering
B. To store and update data that changes over time within the component
C. To style the component dynamically
D. To navigate between pages automatically

Solution

  1. Step 1: Understand what useState does

    useState is a React hook that allows components to keep track of data that can change, like user input or toggles.
  2. Step 2: Identify the purpose in Next.js client components

    In Next.js client components, useState helps remember and update values without reloading the page.
  3. Final Answer:

    To store and update data that changes over time within the component -> Option B
  4. Quick Check:

    State = Remember and update data [OK]
Hint: Remember: useState stores changing data inside components [OK]
Common Mistakes:
  • Thinking useState fetches server data
  • Confusing useState with styling or navigation
  • Forgetting useState only works in client components
2. Which of the following is the correct way to declare a state variable called count with initial value 0 in a Next.js client component?
easy
A. const [count, setCount] = useState(0);
B. const count = useState(0);
C. let count = useState(0);
D. const count = useState()[0];

Solution

  1. Step 1: Recall the syntax of useState hook

    The correct syntax uses array destructuring: const [state, setState] = useState(initialValue);
  2. Step 2: Match the syntax with the variable name and initial value

    For count starting at 0, it should be const [count, setCount] = useState(0);
  3. Final Answer:

    const [count, setCount] = useState(0); -> Option A
  4. Quick Check:

    useState syntax = [state, setState] [OK]
Hint: Use array destructuring: [value, setValue] = useState() [OK]
Common Mistakes:
  • Assigning useState directly to a single variable
  • Using let instead of const for state
  • Not using array destructuring syntax
3. Consider this Next.js client component code:
"use client";
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
     setCount(count + 1)}>
      Count: {count}
    
  );
}

What will be displayed after clicking the button three times?
medium
A. Count: 0
B. Count: 1
C. Count: 3
D. Count: undefined

Solution

  1. Step 1: Understand initial state and button click effect

    The initial count is 0. Each click calls setCount(count + 1), increasing count by 1.
  2. Step 2: Calculate count after three clicks

    After 1 click: count = 1
    After 2 clicks: count = 2
    After 3 clicks: count = 3
  3. Final Answer:

    Count: 3 -> Option C
  4. Quick Check:

    Increment count 3 times = 3 [OK]
Hint: Each click adds 1 to count state [OK]
Common Mistakes:
  • Thinking count resets on each render
  • Confusing initial value with updated value
  • Ignoring that setCount updates state asynchronously
4. What is wrong with this Next.js client component code?
import { useState } from 'react';

export default function Toggle() {
  const [isOn, setIsOn] = useState(false);
  return (
     setIsOn(!isOn)}>
      {isOn ? 'ON' : 'OFF'}
    
  );
}
medium
A. State variable should be declared with let
B. Incorrect use of useState syntax
C. Button onClick handler is invalid
D. Missing 'use client'; directive at the top

Solution

  1. Step 1: Check for client component requirement

    Hooks like useState require the component to be a client component, which needs 'use client'; at the top.
  2. Step 2: Verify code for missing directive

    The code lacks 'use client'; at the top, so it will cause an error in Next.js.
  3. Final Answer:

    Missing 'use client'; directive at the top -> Option D
  4. Quick Check:

    Client components need 'use client' [OK]
Hint: Always add 'use client'; on top when using hooks [OK]
Common Mistakes:
  • Forgetting 'use client' directive
  • Thinking useState syntax is wrong
  • Assuming onClick handler is invalid
5. You want to create a Next.js client component that toggles between showing and hiding a message when a button is clicked. Which code snippet correctly implements this behavior?
hard
A. "use client"; import { useState } from 'react'; export default function ShowHide() { const [visible, setVisible] = useState(false); return ( <> setVisible(!visible)}> {visible ? 'Hide' : 'Show'} Message {visible &&

Hello, this is the message!

} </> ); }
B. "use client"; import { useState } from 'react'; export default function ShowHide() { const visible = false; return ( <> (visible = !visible)}> {visible ? 'Hide' : 'Show'} Message {visible &&

Hello, this is the message!

} </> ); }
C. import { useState } from 'react'; export default function ShowHide() { const [visible, setVisible] = useState(false); return ( <> setVisible(!visible)}> {visible ? 'Hide' : 'Show'} Message {visible &&

Hello, this is the message!

} </> ); }
D. "use client"; import { useState } from 'react'; export default function ShowHide() { const [visible, setVisible] = useState(false); return ( <> setVisible(visible)}> {visible ? 'Hide' : 'Show'} Message {visible &&

Hello, this is the message!

} </> ); }

Solution

  1. Step 1: Check for 'use client' directive and useState usage

    "use client"; import { useState } from 'react'; export default function ShowHide() { const [visible, setVisible] = useState(false); return ( <> setVisible(!visible)}> {visible ? 'Hide' : 'Show'} Message {visible &&

    Hello, this is the message!

    } </> ); } correctly includes 'use client'; and uses useState to track visibility state.
  2. Step 2: Verify toggle logic and rendering

    The button toggles visible state with setVisible(!visible), and the message shows only when visible is true.
  3. Final Answer:

    Option A code snippet correctly implements the toggle behavior -> Option A
  4. Quick Check:

    Toggle state with useState and 'use client' [OK]
Hint: Use useState and 'use client'; toggle with setVisible(!visible) [OK]
Common Mistakes:
  • Not adding 'use client' directive
  • Trying to change state variable directly
  • Passing current state to setter without toggling
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard