Skip to content

React 状态 State

概述

状态(State)是 React 组件中可以随时间变化的数据。状态使组件能够响应用户交互、网络请求或其他事件。本章将学习如何在函数组件中使用 useState Hook 管理状态。

🔄 什么是状态

状态 vs Props

jsx
// Props 是从外部传入的,只读的
function Greeting({ name }) {
    return <h1>Hello, {name}!</h1>;
}

// State 是组件内部的,可变的
function Counter() {
    const [count, setCount] = useState(0);
    
    return (
        <div>
            <p>当前计数: {count}</p>
            <button onClick={() => setCount(count + 1)}>
                增加
            </button>
        </div>
    );
}

状态的特点

  • 局部性:每个组件实例都有自己的状态
  • 异步更新:状态更新可能是异步的
  • 触发重新渲染:状态改变会导致组件重新渲染

🎣 useState Hook

基础用法

jsx
import { useState } from 'react';

function SimpleCounter() {
    // [状态值, 更新函数] = useState(初始值)
    const [count, setCount] = useState(0);
    
    const increment = () => setCount(count + 1);
    const decrement = () => setCount(count - 1);
    const reset = () => setCount(0);
    
    return (
        <div>
            <h2>计数器: {count}</h2>
            <button onClick={increment}>+1</button>
            <button onClick={decrement}>-1</button>
            <button onClick={reset}>重置</button>
        </div>
    );
}

不同类型的状态

jsx
function StateDemoApp() {
    // 数字状态
    const [age, setAge] = useState(25);
    
    // 字符串状态
    const [name, setName] = useState('');
    
    // 布尔值状态
    const [isVisible, setIsVisible] = useState(true);
    
    // 数组状态
    const [items, setItems] = useState(['苹果', '香蕉']);
    
    // 对象状态
    const [user, setUser] = useState({
        name: '张三',
        email: 'zhangsan@example.com'
    });
    
    const addItem = () => {
        setItems([...items, `新项目 ${items.length + 1}`]);
    };
    
    const updateUser = () => {
        setUser({
            ...user,
            name: '李四'
        });
    };
    
    return (
        <div>
            <h3>姓名: {name}</h3>
            <input 
                value={name}
                onChange={(e) => setName(e.target.value)}
                placeholder="输入姓名"
            />
            
            <h3>年龄: {age}</h3>
            <button onClick={() => setAge(age + 1)}>增加年龄</button>
            
            <button onClick={() => setIsVisible(!isVisible)}>
                {isVisible ? '隐藏' : '显示'}内容
            </button>
            
            {isVisible && (
                <div>
                    <h3>项目列表:</h3>
                    <ul>
                        {items.map((item, index) => (
                            <li key={index}>{item}</li>
                        ))}
                    </ul>
                    <button onClick={addItem}>添加项目</button>
                </div>
            )}
            
            <h3>用户: {user.name} ({user.email})</h3>
            <button onClick={updateUser}>更新用户</button>
        </div>
    );
}

🔄 状态更新模式

函数式更新

jsx
function Counter() {
    const [count, setCount] = useState(0);
    
    const increment = () => {
        // 基于之前的状态值更新
        setCount(prevCount => prevCount + 1);
    };
    
    const incrementByFive = () => {
        // 连续更新时使用函数式更新
        setCount(prev => prev + 1);
        setCount(prev => prev + 1);
        setCount(prev => prev + 1);
        setCount(prev => prev + 1);
        setCount(prev => prev + 1);
    };
    
    return (
        <div>
            <p>计数: {count}</p>
            <button onClick={increment}>+1</button>
            <button onClick={incrementByFive}>+5</button>
        </div>
    );
}

对象状态更新

jsx
function UserProfile() {
    const [user, setUser] = useState({
        name: '',
        email: '',
        age: 0,
        preferences: {
            theme: 'light',
            language: 'zh'
        }
    });
    
    const updateName = (name) => {
        setUser(prevUser => ({
            ...prevUser,
            name
        }));
    };
    
    const updateEmail = (email) => {
        setUser(prevUser => ({
            ...prevUser,
            email
        }));
    };
    
    const updatePreferences = (newPrefs) => {
        setUser(prevUser => ({
            ...prevUser,
            preferences: {
                ...prevUser.preferences,
                ...newPrefs
            }
        }));
    };
    
    return (
        <div>
            <input
                placeholder="姓名"
                value={user.name}
                onChange={(e) => updateName(e.target.value)}
            />
            
            <input
                placeholder="邮箱"
                value={user.email}
                onChange={(e) => updateEmail(e.target.value)}
            />
            
            <select
                value={user.preferences.theme}
                onChange={(e) => updatePreferences({ theme: e.target.value })}
            >
                <option value="light">浅色主题</option>
                <option value="dark">深色主题</option>
            </select>
            
            <div>
                <h3>用户信息:</h3>
                <p>姓名: {user.name}</p>
                <p>邮箱: {user.email}</p>
                <p>主题: {user.preferences.theme}</p>
            </div>
        </div>
    );
}

数组状态更新

jsx
function TodoList() {
    const [todos, setTodos] = useState([
        { id: 1, text: '学习 React', completed: false },
        { id: 2, text: '写代码', completed: true }
    ]);
    const [newTodo, setNewTodo] = useState('');
    
    const addTodo = () => {
        if (newTodo.trim()) {
            setTodos(prevTodos => [
                ...prevTodos,
                {
                    id: Date.now(),
                    text: newTodo,
                    completed: false
                }
            ]);
            setNewTodo('');
        }
    };
    
    const toggleTodo = (id) => {
        setTodos(prevTodos =>
            prevTodos.map(todo =>
                todo.id === id
                    ? { ...todo, completed: !todo.completed }
                    : todo
            )
        );
    };
    
    const deleteTodo = (id) => {
        setTodos(prevTodos => 
            prevTodos.filter(todo => todo.id !== id)
        );
    };
    
    return (
        <div>
            <div>
                <input
                    value={newTodo}
                    onChange={(e) => setNewTodo(e.target.value)}
                    placeholder="添加新任务"
                />
                <button onClick={addTodo}>添加</button>
            </div>
            
            <ul>
                {todos.map(todo => (
                    <li key={todo.id}>
                        <input
                            type="checkbox"
                            checked={todo.completed}
                            onChange={() => toggleTodo(todo.id)}
                        />
                        <span style={{
                            textDecoration: todo.completed ? 'line-through' : 'none'
                        }}>
                            {todo.text}
                        </span>
                        <button onClick={() => deleteTodo(todo.id)}>删除</button>
                    </li>
                ))}
            </ul>
        </div>
    );
}

🎛️ 复杂状态管理

使用 useReducer

jsx
import { useReducer } from 'react';

// 定义 reducer 函数
function counterReducer(state, action) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        case 'reset':
            return { count: 0 };
        case 'set':
            return { count: action.payload };
        default:
            throw new Error(`未知的 action 类型: ${action.type}`);
    }
}

function AdvancedCounter() {
    const [state, dispatch] = useReducer(counterReducer, { count: 0 });
    
    return (
        <div>
            <p>计数: {state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>
                +1
            </button>
            <button onClick={() => dispatch({ type: 'decrement' })}>
                -1
            </button>
            <button onClick={() => dispatch({ type: 'reset' })}>
                重置
            </button>
            <button onClick={() => dispatch({ type: 'set', payload: 100 })}>
                设为 100
            </button>
        </div>
    );
}

自定义 Hook 封装状态逻辑

jsx
// 自定义 Hook:表单状态管理
function useForm(initialValues) {
    const [values, setValues] = useState(initialValues);
    const [errors, setErrors] = useState({});
    
    const handleChange = (name, value) => {
        setValues(prev => ({
            ...prev,
            [name]: value
        }));
        
        // 清除错误
        if (errors[name]) {
            setErrors(prev => ({
                ...prev,
                [name]: ''
            }));
        }
    };
    
    const handleSubmit = (onSubmit, validationRules = {}) => {
        return (e) => {
            e.preventDefault();
            
            const newErrors = {};
            Object.keys(validationRules).forEach(field => {
                const rule = validationRules[field];
                if (rule.required && !values[field]) {
                    newErrors[field] = `${rule.label || field} 是必填项`;
                }
            });
            
            setErrors(newErrors);
            
            if (Object.keys(newErrors).length === 0) {
                onSubmit(values);
            }
        };
    };
    
    const reset = () => {
        setValues(initialValues);
        setErrors({});
    };
    
    return {
        values,
        errors,
        handleChange,
        handleSubmit,
        reset
    };
}

// 使用自定义 Hook
function ContactForm() {
    const { values, errors, handleChange, handleSubmit, reset } = useForm({
        name: '',
        email: '',
        message: ''
    });
    
    const onSubmit = (formData) => {
        console.log('提交表单:', formData);
        alert('表单提交成功!');
        reset();
    };
    
    const validationRules = {
        name: { required: true, label: '姓名' },
        email: { required: true, label: '邮箱' },
        message: { required: true, label: '消息' }
    };
    
    return (
        <form onSubmit={handleSubmit(onSubmit, validationRules)}>
            <div>
                <input
                    placeholder="姓名"
                    value={values.name}
                    onChange={(e) => handleChange('name', e.target.value)}
                />
                {errors.name && <span className="error">{errors.name}</span>}
            </div>
            
            <div>
                <input
                    type="email"
                    placeholder="邮箱"
                    value={values.email}
                    onChange={(e) => handleChange('email', e.target.value)}
                />
                {errors.email && <span className="error">{errors.email}</span>}
            </div>
            
            <div>
                <textarea
                    placeholder="消息"
                    value={values.message}
                    onChange={(e) => handleChange('message', e.target.value)}
                />
                {errors.message && <span className="error">{errors.message}</span>}
            </div>
            
            <button type="submit">提交</button>
            <button type="button" onClick={reset}>重置</button>
        </form>
    );
}

⚡ 状态性能优化

状态分割

jsx
// ❌ 所有状态都在一个对象中
function BadComponent() {
    const [state, setState] = useState({
        user: null,
        posts: [],
        loading: false,
        theme: 'light',
        sidebar: false
    });
    
    // 任何一个字段的更新都会导致整个组件重新渲染
}

// ✅ 将不相关的状态分开
function GoodComponent() {
    const [user, setUser] = useState(null);
    const [posts, setPosts] = useState([]);
    const [loading, setLoading] = useState(false);
    const [theme, setTheme] = useState('light');
    const [sidebar, setSidebar] = useState(false);
    
    // 每个状态独立更新,减少不必要的重新渲染
}

使用 useMemo 和 useCallback 优化

jsx
import { useState, useMemo, useCallback } from 'react';

function OptimizedList({ items }) {
    const [filter, setFilter] = useState('');
    const [sortBy, setSortBy] = useState('name');
    
    // 缓存过滤和排序的结果
    const filteredAndSortedItems = useMemo(() => {
        const filtered = items.filter(item =>
            item.name.toLowerCase().includes(filter.toLowerCase())
        );
        
        return filtered.sort((a, b) => {
            if (sortBy === 'name') {
                return a.name.localeCompare(b.name);
            }
            return a[sortBy] - b[sortBy];
        });
    }, [items, filter, sortBy]);
    
    // 缓存事件处理函数
    const handleFilterChange = useCallback((e) => {
        setFilter(e.target.value);
    }, []);
    
    const handleSortChange = useCallback((e) => {
        setSortBy(e.target.value);
    }, []);
    
    return (
        <div>
            <input
                placeholder="搜索..."
                value={filter}
                onChange={handleFilterChange}
            />
            
            <select value={sortBy} onChange={handleSortChange}>
                <option value="name">按名称排序</option>
                <option value="price">按价格排序</option>
            </select>
            
            <ul>
                {filteredAndSortedItems.map(item => (
                    <li key={item.id}>
                        {item.name} - ${item.price}
                    </li>
                ))}
            </ul>
        </div>
    );
}

📝 本章小结

状态管理是 React 应用的核心,正确使用状态能够构建响应式和交互式的用户界面。

关键要点

  • ✅ useState Hook 用于管理组件状态
  • ✅ 状态更新是异步的,使用函数式更新确保正确性
  • ✅ 对象和数组状态需要不可变更新
  • ✅ useReducer 适合复杂状态逻辑
  • ✅ 自定义 Hook 可以封装状态逻辑
  • ✅ 状态分割和缓存可以优化性能

最佳实践

  1. 保持状态最小化:只存储必要的状态
  2. 状态结构扁平化:避免深层嵌套
  3. 使用函数式更新:确保状态更新的正确性
  4. 合理分割状态:提高组件性能
  5. 封装复杂逻辑:使用自定义 Hook

继续学习下一章 - React 事件处理

本站内容仅供学习和研究使用。