React: State Basics: useState
Last updated: 2026-10-26
State is a component’s memory. If Props are parameters given to you by others (read-only), then State is your own little notebook (editable). useState is the “memory chip” that React installs in a component.
1. What You'll Learn
- What is State, and why is State needed?
- Basic Usage of useState
- The Correct Way to Update Array and Object States (Immutability)
- Managing Multiple State Variables
- Lifting State Up
2. Reflections Triggered by a Counter
(1) Pain Point: Regular variables do not trigger UI updates
Bob wants to make a simple counter: when you click the button, the number increases by 1.
▶ Try it YourselfBob discovered a problem: Ordinary variables do not notify React to update the UI. Every time the component re-renders, count is reset to 0.
(2) A Solution Using useState
▶ Try it Yourself
Return Value: useState returns an array [current value, setter function]. After calling setCount, React automatically re-renders the component to display the latest values.
3. Core Concepts of useState
(1) Deconstructing Assignment
import { useState } from 'react'
// useState(initialValue) Back [value, setValue]
const [count, setCount] = useState(0)
// ^ ^ ^
// | | └─ Initial value(Applies only to the first render)
// | └─ Update Function(Triggers a re-render after being called)
// └─ Current status value(Get the latest value on every render)
| Section | Description |
|---|---|
count |
Current state value; the latest value is retrieved with each render |
setCount |
Update function: Calling it → React re-renders the component |
useState(0) |
Initial value (used during the first render; ignored in subsequent renders) |
(2) Update Mechanism
▶ Try it YourselfKey Concept:
setCount(count + 1)means "ask React to updatecounton the next render," not "immediately changecount." This is called asynchronous updating.
(3) Functional Updates
If the new state depends on the old state, you should use functional updates:
▶ Try it Yourself| Method | Syntax | Characteristics |
|---|---|---|
| Update Directly | setCount(count + 1) |
Simple, but data is lost with consecutive calls |
| Functional Update | setCount(prev => prev + 1) |
Accurate; works correctly even with consecutive calls |
4. Updating Arrays and Objects (Immutability)
React requires that state not be modified directly. Never use state.push() or state.name = 'xxx'; instead, create a new array or object to replace the old value.
(1) Objects to Update
▶ Try it Yourself(2) Update the array
▶ Try it YourselfOutput:
State: arr
▶ Example: Array Operations Quick Reference Table
// ============================================
// Example:React Array Operations in C 6 Common Scenarios
// ============================================
const [arr, setArr] = useState([1, 2, 3])
// 1. Append to the end
setArr([...arr, 4]) // [1, 2, 3, 4]
// 2. Add to the beginning
setArr([0, ...arr]) // [0, 1, 2, 3]
// 3. Insert in the middle
const insertAt = 1
setArr([...arr.slice(0, insertAt), 99, ...arr.slice(insertAt)])
// [1, 99, 2, 3]
// 4. Delete Element(filter)
setArr(arr.filter(n => n !== 2)) // [1, 3]
// 5. Update Element(map)
setArr(arr.map(n => n === 2 ? 22 : n)) // [1, 22, 3]
// 6. Sort(Copy first, then sort)
setArr([...arr].sort((a, b) => b - a)) // [3, 2, 1]
Output:
Array state operations: push → add item, filter → remove item, map → update item. Each returns a new array reference for React re-rendering
5. Multiple State Variables
A component can have multiple state variables. It is recommended to break them down by logic rather than cramming them into a single large object:
function RegistrationForm() {
// ✅ Recommendations:Logical Breakdown
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const [errors, setErrors] = useState({})
// ❌ Not recommended:All states are stored in a single object
const [form, setForm] = useState({
name: '', email: '', password: '',
isSubmitting: false, errors: {}
})
// Expand the entire object when updating,Fields that are easy to overlook
}
| Strategy | Advantages | Disadvantages |
|---|---|---|
Split into multiple useStates |
Precise updates, type-safe, and easy to read | Too many variables when there's a lot of state |
Combined into a single useState |
Convenient for updating multiple fields at once | The update syntax is cumbersome and it's easy to forget to expand it |
Recommendation: Use multiple
useStateinstances for independent states (such as input fields, loading indicators, and error messages); logically related fields (such as all fields in a user profile) can be combined into a single object.
6. Lifting State Up
When multiple components need to share the same state, lift the state up to their nearest common parent component.
graph TB
subgraph "Before the status upgrade(Data is not shared)"
A[Parent Component App] --- B[Child component A<br/>Have their own count]
A --- C[Child component B<br/>Have their own count]
end
subgraph "After the status upgrade(Data Sharing)"
D[Parent Component App<br/>**count Here**] --- E[Child component A<br/>Read count, Call setCount]
D --- F[Child component B<br/>Read count, Call setCount]
end
Output:
State: arr (setter: setArr)
▶ Example: Practical Application of State Promotion
▶ Try it YourselfOutput:
Enter 100 in Celsius → Fahrenheit auto-shows 212.0. Displays "100°C = 212.0°F". Two inputs stay synchronized.
7. Complete Example: Shopping Cart Counter
▶ Expand runnable example (96 lines)
Output:
Subheading: "Temperature Converter". Displays: "Celsius(°C):". State: temperature (setter: setTemperature)
Interaction Flow:
- Tap +/− to adjust the quantity; the subtotal and total are updated in real time
- Click ✕ to delete an item; display a friendly prompt when the shopping cart is empty
- Enter the promo code
REACT2026to save 20 yuan
▶ Example 3: Updating an Object's State—Editing User Information
function UserEditor() {
const [user, setUser] = useState({ name: 'Alice', email: 'alice@test.com', age: 28 })
function updateField(field, value) {
setUser(prev => ({ ...prev, [field]: value }))
}
return (
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<h3>Edit Profile</h3>
<input value={user.name} onChange={e => updateField('name', e.target.value)}
placeholder="Name" style={{ width: '100%', padding: 8, marginBottom: 8, borderRadius: 4 }} />
<input value={user.email} onChange={e => updateField('email', e.target.value)}
placeholder="Email" style={{ width: '100%', padding: 8, marginBottom: 8, borderRadius: 4 }} />
<input type="number" value={user.age} onChange={e => updateField('age', Number(e.target.value))}
placeholder="Age" style={{ width: '100%', padding: 8, marginBottom: 8, borderRadius: 4 }} />
<pre style={{ background: '#f5f5f5', padding: 12, borderRadius: 4, fontSize: 13 }}>
{JSON.stringify(user, null, 2)}
</pre>
</div>
)
}
Output:
State: address. Inputs: Street, City, ZIP
Output:
Displays: "Edit Profile". State: user (setter: setUser). Input: Name, Email, Age
▶ Example 4: Nested Object States—Address Management
▶ Try it YourselfOutput:
Shipping form: Street, City, ZIP, Country dropdown (US/CN/JP), "Primary address" checkbox. All fields update state reactively.
Output:
Displays: "Shipping Address". State: address (setter: setAddress). Input: Street, City, ZIP. Dropdown: United States, China, Japan
▶ Example 5: Functional Updates Solve the Problem of Batch Updates
▶ Try it YourselfOutput:
Score: 0, Multiplier: x1. +10/+50/+100 add points×multiplier. "2x Multiplier" doubles (max x8). Reset → Score: 0, x1
❓ FAQ
useState and regular variables?useState incurs additional overhead (tracking changes, scheduling rendering), so it should only be used in scenarios where "UI updates are triggered when changes occur." If a variable changes but does not require a UI update (such as a timer ID or scroll position), using useRef (Lesson 10) is more appropriate.useStates can there be in a single component? Are there any performance issues?useStates based on logic rather than using a single large object. Each useState call is a "state unit," and React can efficiently handle a large number of useStates. Common components typically have 3–8 useStates.setState update synchronous or asynchronous?setState was "automatically batched" within event handlers, but synchronous within setTimeout and Promise calls. React 18 introduced Automatic Batching, so setState is now batch-asynchronous regardless of whether it’s called within an event handler, setTimeout, Promise, or fetch callback. If you absolutely need to access the updated DOM synchronously, you can use flushSync(() => setState(...)).📖 Summary
- State is a component's "memory"; when it changes, it automatically triggers a UI update
useState(initialValue)Returns the[value, setValue]array- Immutability: Never modify the state directly; instead, create a new object or array to replace it.
- Functional update
setCount(prev => prev + 1)is more accurate when relying on old values - State Elevation: When multiple child components need to share data, the state is elevated to a common parent component.
📝 Exercises
- Basic Exercise (Difficulty ⭐): Create a
LikeButtoncomponent that toggles between ❤️ and 🤍 states when clicked and displays the number of likes. - Advanced Exercise (Difficulty ⭐⭐): Create a
ExpenseTrackercomponent that usesuseStateto manage a list of expenses (description, amount, date), and supports adding and removing items. - Challenge (Difficulty: ⭐⭐⭐): Create a
ColorPickercomponent that uses threeuseStateinstances to manage the R, G, and B slider values (0–255), and displays a real-time color preview and hex color code.