当前位置: 首页 > news >正文

React 18.x 学习计划 - 第四天:React Hooks深入

学习目标

  • 深入理解useState Hook
  • 掌握useEffect Hook的使用
  • 学会useContext Hook
  • 理解自定义Hooks
  • 掌握Hooks规则和最佳实践

学习时间安排

总时长:6-7小时

  • useState深入:1.5小时
  • useEffect详解:2小时
  • useContext和useReducer:1.5小时
  • 自定义Hooks:1.5小时
  • 实践项目:1.5-2小时

第一部分:useState Hook深入 (1.5小时)

1.1 useState基础回顾

基本用法(详细注释版)
// src/components/UseStateBasic.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义UseStateBasic组件
function UseStateBasic() {// 使用useState Hook管理状态// useState返回一个数组,包含当前状态值和更新状态的函数const [count, setCount] = useState(0);// 定义增加计数器的函数const increment = () => {// 使用setCount函数更新状态setCount(count + 1);};// 定义减少计数器的函数const decrement = () => {setCount(count - 1);};// 定义重置计数器的函数const reset = () => {setCount(0);};// 返回JSX元素return (<div><h2>useState Basic Example</h2><p>Count: {count}</p><button onClick={increment}>+</button><button onClick={decrement}>-</button><button onClick={reset}>Reset</button></div>);
}// 导出组件
export default UseStateBasic;

1.2 函数式更新

函数式更新示例(详细注释版)
// src/components/UseStateFunctional.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义UseStateFunctional组件
function UseStateFunctional() {// 使用useState Hook管理状态const [count, setCount] = useState(0);// 使用函数式更新// 这种方式可以确保获取到最新的状态值const increment = () => {// 使用函数式更新,prevCount是前一个状态值setCount(prevCount => prevCount + 1);};// 连续更新示例const incrementByTwo = () => {// 使用函数式更新确保每次更新都基于最新状态setCount(prevCount => prevCount + 1);setCount(prevCount => prevCount + 1);};// 异步更新示例const asyncIncrement = () => {// 模拟异步操作setTimeout(() => {// 使用函数式更新确保获取最新状态setCount(prevCount => prevCount + 1);}, 1000);};// 返回JSX元素return (<div><h2>useState Functional Updates</h2><p>Count: {count}</p><button onClick={increment}>+1</button><button onClick={incrementByTwo}>+2</button><button onClick={asyncIncrement}>Async +1</button></div>);
}// 导出组件
export default UseStateFunctional;

1.3 对象状态管理

对象状态示例(详细注释版)
// src/components/UseStateObject.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义UseStateObject组件
function UseStateObject() {// 使用useState Hook管理对象状态const [user, setUser] = useState({name: '',email: '',age: 0,isActive: false});// 更新用户信息的函数const updateUser = (field, value) => {// 使用展开运算符保持其他属性不变setUser(prevUser => ({...prevUser,[field]: value}));};// 重置用户信息const resetUser = () => {setUser({name: '',email: '',age: 0,isActive: false});};// 返回JSX元素return (<div><h2>useState Object Management</h2>{/* 用户信息显示 */}<div><h3>User Information</h3><p>Name: {user.name}</p><p>Email: {user.email}</p><p>Age: {user.age}</p><p>Active: {user.isActive ? 'Yes' : 'No'}</p></div>{/* 用户信息输入表单 */}<div><h3>Update User</h3><inputtype="text"placeholder="Name"value={user.name}onChange={(e) => updateUser('name', e.target.value)}/><inputtype="email"placeholder="Email"value={user.email}onChange={(e) => updateUser('email', e.target.value)}/><inputtype="number"placeholder="Age"value={user.age}onChange={(e) => updateUser('age', parseInt(e.target.value) || 0)}/><label><inputtype="checkbox"checked={user.isActive}onChange={(e) => updateUser('isActive', e.target.checked)}/>Active</label><button onClick={resetUser}>Reset</button></div></div>);
}// 导出组件
export default UseStateObject;

1.4 数组状态管理

数组状态示例(详细注释版)
// src/components/UseStateArray.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义UseStateArray组件
function UseStateArray() {// 使用useState Hook管理数组状态const [items, setItems] = useState([]);const [newItem, setNewItem] = useState('');// 添加新项目const addItem = () => {if (newItem.trim()) {// 使用展开运算符添加新项目setItems(prevItems => [...prevItems, {id: Date.now(),text: newItem.trim(),completed: false}]);setNewItem('');}};// 删除项目const deleteItem = (id) => {// 使用filter方法删除指定项目setItems(prevItems => prevItems.filter(item => item.id !== id));};// 切换项目完成状态const toggleItem = (id) => {// 使用map方法更新指定项目setItems(prevItems => prevItems.map(item =>item.id === id ? { ...item, completed: !item.completed } : item));};// 清空所有项目const clearAll = () => {setItems([]);};// 返回JSX元素return (<div><h2>useState Array Management</h2>{/* 添加新项目 */}<div><inputtype="text"placeholder="Add new item"value={newItem}onChange={(e) => setNewItem(e.target.value)}onKeyPress={(e) => e.key === 'Enter' && addItem()}/><button onClick={addItem}>Add Item</button></div>{/* 项目列表 */}<div>{items.length === 0 ? (<p>No items yet</p>) : (items.map(item => (<div key={item.id} style={{ display: 'flex', alignItems: 'center', gap: '10px' }}><inputtype="checkbox"checked={item.completed}onChange={() => toggleItem(item.id)}/><span style={{ textDecoration: item.completed ? 'line-through' : 'none' }}>{item.text}</span><button onClick={() => deleteItem(item.id)}>Delete</button></div>)))}</div>{/* 操作按钮 */}<div><button onClick={clearAll}>Clear All</button><p>Total items: {items.length}</p><p>Completed: {items.filter(item => item.completed).length}</p></div></div>);
}// 导出组件
export default UseStateArray;

第二部分:useEffect Hook详解 (2小时)

2.1 useEffect基础

基本用法(详细注释版)
// src/components/UseEffectBasic.js
// 导入React、useState和useEffect Hook
import React, { useState, useEffect } from 'react';// 定义UseEffectBasic组件
function UseEffectBasic() {// 使用useState Hook管理状态const [count, setCount] = useState(0);const [name, setName] = useState('');// useEffect Hook - 每次渲染后都会执行useEffect(() => {// 这个函数在每次组件渲染后都会执行console.log('Component rendered');});// useEffect Hook - 只在组件挂载时执行一次useEffect(() => {// 这个函数只在组件第一次挂载时执行console.log('Component mounted');// 清理函数,在组件卸载时执行return () => {console.log('Component unmounted');};}, []); // 空依赖数组表示只在挂载和卸载时执行// useEffect Hook - 依赖count变化时执行useEffect(() => {// 这个函数在count变化时执行console.log('Count changed:', count);}, [count]); // 依赖数组包含count// useEffect Hook - 依赖name变化时执行useEffect(() => {// 这个函数在name变化时执行console.log('Name changed:', name);}, [name]); // 依赖数组包含name// 返回JSX元素return (<div><h2>useEffect Basic Example</h2><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button><inputtype="text"placeholder="Enter name"value={name}onChange={(e) => setName(e.target.value)}/></div>);
}// 导出组件
export default UseEffectBasic;

2.2 数据获取

数据获取示例(详细注释版)
// src/components/UseEffectDataFetching.js
// 导入React、useState和useEffect Hook
import React, { useState, useEffect } from 'react';// 定义UseEffectDataFetching组件
function UseEffectDataFetching() {// 使用useState Hook管理状态const [users, setUsers] = useState([]);const [loading, setLoading] = useState(false);const [error, setError] = useState(null);const [userId, setUserId] = useState(1);// 获取用户列表const fetchUsers = async () => {setLoading(true);setError(null);try {// 模拟API调用const response = await fetch('https://jsonplaceholder.typicode.com/users');if (!response.ok) {throw new Error('Failed to fetch users');}const data = await response.json();setUsers(data);} catch (err) {setError(err.message);} finally {setLoading(false);}};// 获取单个用户const fetchUser = async (id) => {setLoading(true);setError(null);try {const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);if (!response.ok) {throw new Error('Failed to fetch user');}const data = await response.json();setUsers([data]);} catch (err) {setError(err.message);} finally {setLoading(false);}};// useEffect Hook - 组件挂载时获取用户列表useEffect(() => {fetchUsers();}, []); // 空依赖数组,只在挂载时执行// useEffect Hook - userId变化时获取单个用户useEffect(() => {if (userId) {fetchUser(userId);}}, [userId]); // 依赖userId// 返回JSX元素return (<div><h2>useEffect Data Fetching</h2>{/* 用户ID输入 */}<div><inputtype="number"placeholder="User ID"value={userId}onChange={(e) => setUserId(parseInt(e.target.value) || 1)}/><button onClick={() => fetchUsers()}>Fetch All Users</button></div>{/* 加载状态 */}{loading && <p>Loading...</p>}{/* 错误状态 */}{error && <p style={{ color: 'red' }}>Error: {error}</p>}{/* 用户列表 */}{users.length > 0 && (<div><h3>Users:</h3>{users.map(user => (<div key={user.id} style={{ border: '1px solid #ccc', padding: '10px', margin: '5px' }}><h4>{user.name}</h4><p>Email: {user.email}</p><p>Phone: {user.phone}</p><p>Website: {user.website}</p></div>))}</div>)}</div>);
}// 导出组件
export default UseEffectDataFetching;

2.3 清理函数

清理函数示例(详细注释版)
// src/components/UseEffectCleanup.js
// 导入React、useState和useEffect Hook
import React, { useState, useEffect } from 'react';// 定义UseEffectCleanup组件
function UseEffectCleanup() {// 使用useState Hook管理状态const [count, setCount] = useState(0);const [isVisible, setIsVisible] = useState(true);// useEffect Hook - 设置定时器useEffect(() => {// 创建定时器const timer = setInterval(() => {setCount(prevCount => prevCount + 1);}, 1000);// 清理函数,在组件卸载或依赖变化时执行return () => {console.log('Cleaning up timer');clearInterval(timer);};}, []); // 空依赖数组,只在挂载时执行// useEffect Hook - 监听窗口大小变化useEffect(() => {// 定义窗口大小变化处理函数const handleResize = () => {console.log('Window resized');};// 添加事件监听器window.addEventListener('resize', handleResize);// 清理函数,移除事件监听器return () => {console.log('Removing resize listener');window.removeEventListener('resize', handleResize);};}, []); // 空依赖数组,只在挂载时执行// useEffect Hook - 条件执行useEffect(() => {if (isVisible) {console.log('Component is visible');// 返回清理函数return () => {console.log('Component is no longer visible');};}}, [isVisible]); // 依赖isVisible// 返回JSX元素return (<div><h2>useEffect Cleanup Example</h2><p>Count: {count}</p><p>Component is {isVisible ? 'visible' : 'hidden'}</p><button onClick={() => setIsVisible(!isVisible)}>Toggle Visibility</button></div>);
}// 导出组件
export default UseEffectCleanup;

2.4 依赖数组详解

依赖数组示例(详细注释版)
// src/components/UseEffectDependencies.js
// 导入React、useState和useEffect Hook
import React, { useState, useEffect } from 'react';// 定义UseEffectDependencies组件
function UseEffectDependencies() {// 使用useState Hook管理状态const [count, setCount] = useState(0);const [name, setName] = useState('');const [users, setUsers] = useState([]);// useEffect Hook - 无依赖数组,每次渲染都执行useEffect(() => {console.log('Effect without dependencies - runs on every render');});// useEffect Hook - 空依赖数组,只在挂载时执行useEffect(() => {console.log('Effect with empty dependencies - runs only on mount');}, []);// useEffect Hook - 依赖count,count变化时执行useEffect(() => {console.log('Effect with count dependency - runs when count changes');}, [count]);// useEffect Hook - 依赖name,name变化时执行useEffect(() => {console.log('Effect with name dependency - runs when name changes');}, [name]);// useEffect Hook - 依赖多个值,任一值变化时执行useEffect(() => {console.log('Effect with multiple dependencies - runs when count or name changes');}, [count, name]);// useEffect Hook - 复杂依赖useEffect(() => {// 模拟API调用const fetchData = async () => {try {const response = await fetch(`https://jsonplaceholder.typicode.com/users?_limit=${count}`);const data = await response.json();setUsers(data);} catch (error) {console.error('Error fetching data:', error);}};if (count > 0) {fetchData();}}, [count]); // 依赖count// 返回JSX元素return (<div><h2>useEffect Dependencies Example</h2><div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment Count</button></div><div><inputtype="text"placeholder="Enter name"value={name}onChange={(e) => setName(e.target.value)}/></div><div><h3>Users ({users.length}):</h3>{users.map(user => (<div key={user.id}><p>{user.name} - {user.email}</p></div>))}</div></div>);
}// 导出组件
export default UseEffectDependencies;

第三部分:useContext和useReducer (1.5小时)

3.1 useContext Hook

创建Context(详细注释版)
// src/context/ThemeContext.js
// 导入React和createContext
import React, { createContext, useState } from 'react';// 创建ThemeContext
// createContext创建一个上下文对象,用于在组件树中共享数据
export const ThemeContext = createContext();// 定义ThemeProvider组件
// 这个组件提供主题数据给所有子组件
export function ThemeProvider({ children }) {// 使用useState Hook管理主题状态const [theme, setTheme] = useState('light');// 切换主题的函数const toggleTheme = () => {setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');};// 定义上下文值// 这个对象包含所有需要共享的数据和函数const contextValue = {theme,toggleTheme,isDark: theme === 'dark'};// 返回ThemeContext.Provider// 所有子组件都可以通过useContext访问这些数据return (<ThemeContext.Provider value={contextValue}>{children}</ThemeContext.Provider>);
}
使用Context(详细注释版)
// src/components/ThemeConsumer.js
// 导入React和useContext Hook
import React, { useContext } from 'react';
// 导入ThemeContext
import { ThemeContext } from '../context/ThemeContext';// 定义ThemeConsumer组件
function ThemeConsumer() {// 使用useContext Hook获取主题数据// useContext接收一个上下文对象,返回该上下文的当前值const { theme, toggleTheme, isDark } = useContext(ThemeContext);// 定义样式对象const styles = {backgroundColor: isDark ? '#333' : '#fff',color: isDark ? '#fff' : '#333',padding: '20px',border: '1px solid #ccc',borderRadius: '8px'};// 返回JSX元素return (<div style={styles}><h2>Theme Consumer</h2><p>Current theme: {theme}</p><p>Is dark theme: {isDark ? 'Yes' : 'No'}</p><button onClick={toggleTheme}>Switch to {isDark ? 'Light' : 'Dark'} Theme</button></div>);
}// 导出组件
export default ThemeConsumer;

3.2 useReducer Hook

useReducer基础(详细注释版)
// src/components/UseReducerBasic.js
// 导入React和useReducer Hook
import React, { useReducer } from 'react';// 定义初始状态
const initialState = {count: 0,name: '',items: []
};// 定义reducer函数
// reducer函数接收当前状态和动作,返回新状态
function counterReducer(state, action) {// 使用switch语句处理不同的动作类型switch (action.type) {case 'INCREMENT':return {...state,count: state.count + 1};case 'DECREMENT':return {...state,count: state.count - 1};case 'RESET':return {...state,count: 0};case 'SET_NAME':return {...state,name: action.payload};case 'ADD_ITEM':return {...state,items: [...state.items, action.payload]};case 'REMOVE_ITEM':return {...state,items: state.items.filter(item => item.id !== action.payload)};default:// 如果动作类型不匹配,返回当前状态return state;}
}// 定义UseReducerBasic组件
function UseReducerBasic() {// 使用useReducer Hook// useReducer接收reducer函数和初始状态,返回当前状态和dispatch函数const [state, dispatch] = useReducer(counterReducer, initialState);// 定义动作函数const increment = () => {dispatch({ type: 'INCREMENT' });};const decrement = () => {dispatch({ type: 'DECREMENT' });};const reset = () => {dispatch({ type: 'RESET' });};const setName = (name) => {dispatch({ type: 'SET_NAME', payload: name });};const addItem = (item) => {dispatch({ type: 'ADD_ITEM', payload: item });};const removeItem = (id) => {dispatch({ type: 'REMOVE_ITEM', payload: id });};// 返回JSX元素return (<div><h2>useReducer Basic Example</h2><div><p>Count: {state.count}</p><button onClick={increment}>+</button><button onClick={decrement}>-</button><button onClick={reset}>Reset</button></div><div><inputtype="text"placeholder="Enter name"value={state.name}onChange={(e) => setName(e.target.value)}/><p>Name: {state.name}</p></div><div><p>Items: {state.items.length}</p>{state.items.map(item => (<div key={item.id}><span>{item.text}</span><button onClick={() => removeItem(item.id)}>Remove</button></div>))}</div></div>);
}// 导出组件
export default UseReducerBasic;

第四部分:自定义Hooks (1.5小时)

4.1 自定义Hook基础

自定义useCounter Hook(详细注释版)
// src/hooks/useCounter.js
// 导入React和useState Hook
import { useState } from 'react';// 定义自定义useCounter Hook
// 自定义Hook是一个以"use"开头的函数,可以调用其他Hook
function useCounter(initialValue = 0) {// 使用useState Hook管理计数状态const [count, setCount] = useState(initialValue);// 定义增加计数的函数const increment = () => {setCount(prevCount => prevCount + 1);};// 定义减少计数的函数const decrement = () => {setCount(prevCount => prevCount - 1);};// 定义重置计数的函数const reset = () => {setCount(initialValue);};// 定义设置计数的函数const setCountValue = (value) => {setCount(value);};// 返回状态和函数// 自定义Hook可以返回任何值return {count,increment,decrement,reset,setCount: setCountValue};
}// 导出自定义Hook
export default useCounter;
使用自定义Hook(详细注释版)
// src/components/CustomHookExample.js
// 导入React
import React from 'react';
// 导入自定义Hook
import useCounter from '../hooks/useCounter';// 定义CustomHookExample组件
function CustomHookExample() {// 使用自定义useCounter Hook// 自定义Hook的使用方式与内置Hook相同const counter1 = useCounter(0);const counter2 = useCounter(10);// 返回JSX元素return (<div><h2>Custom Hook Example</h2><div><h3>Counter 1</h3><p>Count: {counter1.count}</p><button onClick={counter1.increment}>+</button><button onClick={counter1.decrement}>-</button><button onClick={counter1.reset}>Reset</button></div><div><h3>Counter 2</h3><p>Count: {counter2.count}</p><button onClick={counter2.increment}>+</button><button onClick={counter2.decrement}>-</button><button onClick={counter2.reset}>Reset</button></div></div>);
}// 导出组件
export default CustomHookExample;

4.2 自定义useFetch Hook

useFetch Hook(详细注释版)
// src/hooks/useFetch.js
// 导入React、useState和useEffect Hook
import { useState, useEffect } from 'react';// 定义自定义useFetch Hook
function useFetch(url, options = {}) {// 使用useState Hook管理状态const [data, setData] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);// 使用useEffect Hook处理数据获取useEffect(() => {// 定义获取数据的异步函数const fetchData = async () => {try {// 设置加载状态setLoading(true);setError(null);// 发起HTTP请求const response = await fetch(url, options);// 检查响应状态if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}// 解析响应数据const result = await response.json();setData(result);} catch (err) {// 处理错误setError(err.message);} finally {// 设置加载完成setLoading(false);}};// 调用获取数据函数fetchData();}, [url, JSON.stringify(options)]); // 依赖url和options// 返回状态和函数return { data, loading, error };
}// 导出自定义Hook
export default useFetch;
使用useFetch Hook(详细注释版)
// src/components/UseFetchExample.js
// 导入React
import React, { useState } from 'react';
// 导入自定义Hook
import useFetch from '../hooks/useFetch';// 定义UseFetchExample组件
function UseFetchExample() {// 使用useState Hook管理URL状态const [url, setUrl] = useState('https://jsonplaceholder.typicode.com/users');const [userId, setUserId] = useState(1);// 使用自定义useFetch Hookconst { data, loading, error } = useFetch(url);// 获取单个用户const fetchUser = (id) => {setUrl(`https://jsonplaceholder.typicode.com/users/${id}`);};// 获取所有用户const fetchAllUsers = () => {setUrl('https://jsonplaceholder.typicode.com/users');};// 返回JSX元素return (<div><h2>useFetch Hook Example</h2>{/* 控制按钮 */}<div><inputtype="number"placeholder="User ID"value={userId}onChange={(e) => setUserId(parseInt(e.target.value) || 1)}/><button onClick={() => fetchUser(userId)}>Fetch User</button><button onClick={fetchAllUsers}>Fetch All Users</button></div>{/* 加载状态 */}{loading && <p>Loading...</p>}{/* 错误状态 */}{error && <p style={{ color: 'red' }}>Error: {error}</p>}{/* 数据显示 */}{data && (<div><h3>Data:</h3>{Array.isArray(data) ? (<div><p>Found {data.length} users</p>{data.map(user => (<div key={user.id} style={{ border: '1px solid #ccc', padding: '10px', margin: '5px' }}><h4>{user.name}</h4><p>Email: {user.email}</p><p>Phone: {user.phone}</p></div>))}</div>) : (<div style={{ border: '1px solid #ccc', padding: '10px' }}><h4>{data.name}</h4><p>Email: {data.email}</p><p>Phone: {data.phone}</p><p>Website: {data.website}</p></div>)}</div>)}</div>);
}// 导出组件
export default UseFetchExample;

4.3 自定义useLocalStorage Hook

useLocalStorage Hook(详细注释版)
// src/hooks/useLocalStorage.js
// 导入React和useState Hook
import { useState, useEffect } from 'react';// 定义自定义useLocalStorage Hook
function useLocalStorage(key, initialValue) {// 使用useState Hook管理状态const [storedValue, setStoredValue] = useState(() => {try {// 尝试从localStorage获取值const item = window.localStorage.getItem(key);// 如果存在,解析并返回return item ? JSON.parse(item) : initialValue;} catch (error) {// 如果出错,返回初始值console.error(`Error reading localStorage key "${key}":`, error);return initialValue;}});// 定义设置值的函数const setValue = (value) => {try {// 允许值是一个函数,这样我们可以使用函数式更新const valueToStore = value instanceof Function ? value(storedValue) : value;// 保存状态setStoredValue(valueToStore);// 保存到localStoragewindow.localStorage.setItem(key, JSON.stringify(valueToStore));} catch (error) {// 如果出错,记录错误console.error(`Error setting localStorage key "${key}":`, error);}};// 定义清除值的函数const removeValue = () => {try {// 从localStorage移除window.localStorage.removeItem(key);// 重置为初始值setStoredValue(initialValue);} catch (error) {console.error(`Error removing localStorage key "${key}":`, error);}};// 返回状态和函数return [storedValue, setValue, removeValue];
}// 导出自定义Hook
export default useLocalStorage;
使用useLocalStorage Hook(详细注释版)
// src/components/UseLocalStorageExample.js
// 导入React
import React from 'react';
// 导入自定义Hook
import useLocalStorage from '../hooks/useLocalStorage';// 定义UseLocalStorageExample组件
function UseLocalStorageExample() {// 使用自定义useLocalStorage Hookconst [name, setName, removeName] = useLocalStorage('name', '');const [age, setAge, removeAge] = useLocalStorage('age', 0);const [preferences, setPreferences, removePreferences] = useLocalStorage('preferences', {theme: 'light',language: 'en'});// 返回JSX元素return (<div><h2>useLocalStorage Hook Example</h2>{/* 姓名输入 */}<div><label>Name:<inputtype="text"value={name}onChange={(e) => setName(e.target.value)}/></label><button onClick={removeName}>Clear Name</button></div>{/* 年龄输入 */}<div><label>Age:<inputtype="number"value={age}onChange={(e) => setAge(parseInt(e.target.value) || 0)}/></label><button onClick={removeAge}>Clear Age</button></div>{/* 偏好设置 */}<div><h3>Preferences:</h3><label>Theme:<selectvalue={preferences.theme}onChange={(e) => setPreferences(prev => ({ ...prev, theme: e.target.value }))}><option value="light">Light</option><option value="dark">Dark</option></select></label><label>Language:<selectvalue={preferences.language}onChange={(e) => setPreferences(prev => ({ ...prev, language: e.target.value }))}><option value="en">English</option><option value="es">Spanish</option><option value="fr">French</option></select></label><button onClick={removePreferences}>Clear Preferences</button></div>{/* 显示当前值 */}<div><h3>Current Values:</h3><p>Name: {name}</p><p>Age: {age}</p><p>Theme: {preferences.theme}</p><p>Language: {preferences.language}</p></div></div>);
}// 导出组件
export default UseLocalStorageExample;

第五部分:实践项目(详细注释版)

项目:个人任务管理器

主应用组件(详细注释版)
// src/App.js
// 导入React和useState Hook
import React, { useState } from 'react';
// 导入自定义Hook
import useLocalStorage from './hooks/useLocalStorage';
// 导入组件
import TaskList from './components/TaskList';
import TaskForm from './components/TaskForm';
import TaskStats from './components/TaskStats';
// 导入样式
import './App.css';// 定义主应用组件
function App() {// 使用自定义useLocalStorage Hook管理任务列表const [tasks, setTasks, clearTasks] = useLocalStorage('tasks', []);// 使用useState Hook管理表单状态const [showForm, setShowForm] = useState(false);const [editingTask, setEditingTask] = useState(null);// 添加新任务的函数const addTask = (taskData) => {const newTask = {id: Date.now(),...taskData,createdAt: new Date().toISOString(),updatedAt: new Date().toISOString()};setTasks(prevTasks => [...prevTasks, newTask]);setShowForm(false);};// 更新任务的函数const updateTask = (id, updatedData) => {setTasks(prevTasks => prevTasks.map(task => task.id === id ? { ...task, ...updatedData, updatedAt: new Date().toISOString() }: task));setEditingTask(null);setShowForm(false);};// 删除任务的函数const deleteTask = (id) => {setTasks(prevTasks => prevTasks.filter(task => task.id !== id));};// 切换任务完成状态的函数const toggleTask = (id) => {setTasks(prevTasks => prevTasks.map(task => task.id === id ? { ...task, completed: !task.completed, updatedAt: new Date().toISOString() }: task));};// 编辑任务的函数const editTask = (task) => {setEditingTask(task);setShowForm(true);};// 取消编辑的函数const cancelEdit = () => {setEditingTask(null);setShowForm(false);};// 返回JSX元素return (<div className="App"><header className="App-header"><h1>Personal Task Manager</h1><button className="add-task-btn"onClick={() => setShowForm(true)}>Add New Task</button></header><main>{/* 任务统计 */}<TaskStats tasks={tasks} />{/* 任务表单 */}{showForm && (<TaskFormtask={editingTask}onSubmit={editingTask ? (data) => updateTask(editingTask.id, data) : addTask}onCancel={cancelEdit}/>)}{/* 任务列表 */}<TaskListtasks={tasks}onEdit={editTask}onDelete={deleteTask}onToggle={toggleTask}/>{/* 清除所有任务按钮 */}{tasks.length > 0 && (<button className="clear-all-btn"onClick={clearTasks}>Clear All Tasks</button>)}</main></div>);
}// 导出App组件
export default App;
任务列表组件(详细注释版)
// src/components/TaskList.js
// 导入React和useState Hook
import React, { useState } from 'react';
// 导入TaskItem组件
import TaskItem from './TaskItem';// 定义TaskList组件
function TaskList({ tasks, onEdit, onDelete, onToggle }) {// 使用useState Hook管理过滤状态const [filter, setFilter] = useState('all');const [sortBy, setSortBy] = useState('createdAt');// 过滤任务const filteredTasks = tasks.filter(task => {switch (filter) {case 'completed':return task.completed;case 'pending':return !task.completed;case 'high':return task.priority === 'high';case 'medium':return task.priority === 'medium';case 'low':return task.priority === 'low';default:return true;}});// 排序任务const sortedTasks = [...filteredTasks].sort((a, b) => {switch (sortBy) {case 'title':return a.title.localeCompare(b.title);case 'priority':const priorityOrder = { high: 3, medium: 2, low: 1 };return priorityOrder[b.priority] - priorityOrder[a.priority];case 'createdAt':return new Date(b.createdAt) - new Date(a.createdAt);case 'updatedAt':return new Date(b.updatedAt) - new Date(a.updatedAt);default:return 0;}});// 返回JSX元素return (<div className="task-list"><h2>Tasks ({tasks.length})</h2>{/* 过滤和排序控制 */}<div className="task-controls"><div className="filter-controls"><label>Filter:<select value={filter} onChange={(e) => setFilter(e.target.value)}><option value="all">All Tasks</option><option value="pending">Pending</option><option value="completed">Completed</option><option value="high">High Priority</option><option value="medium">Medium Priority</option><option value="low">Low Priority</option></select></label></div><div className="sort-controls"><label>Sort by:<select value={sortBy} onChange={(e) => setSortBy(e.target.value)}><option value="createdAt">Created Date</option><option value="updatedAt">Updated Date</option><option value="title">Title</option><option value="priority">Priority</option></select></label></div></div>{/* 任务列表 */}<div className="tasks">{sortedTasks.length === 0 ? (<p>No tasks found</p>) : (sortedTasks.map(task => (<TaskItemkey={task.id}task={task}onEdit={onEdit}onDelete={onDelete}onToggle={onToggle}/>)))}</div></div>);
}// 导出TaskList组件
export default TaskList;
任务项组件(详细注释版)
// src/components/TaskItem.js
// 导入React
import React from 'react';// 定义TaskItem组件
function TaskItem({ task, onEdit, onDelete, onToggle }) {// 获取任务属性const { id, title, description, priority, completed, createdAt, updatedAt } = task;// 格式化日期const formatDate = (dateString) => {return new Date(dateString).toLocaleDateString();};// 获取优先级样式const getPriorityClass = (priority) => {switch (priority) {case 'high':return 'priority-high';case 'medium':return 'priority-medium';case 'low':return 'priority-low';default:return '';}};// 返回JSX元素return (<div className={`task-item ${completed ? 'completed' : ''} ${getPriorityClass(priority)}`}>{/* 任务头部 */}<div className="task-header"><div className="task-title"><inputtype="checkbox"checked={completed}onChange={() => onToggle(id)}/><h3>{title}</h3></div><div className="task-actions"><button className="edit-btn"onClick={() => onEdit(task)}>Edit</button><button className="delete-btn"onClick={() => onDelete(id)}>Delete</button></div></div>{/* 任务描述 */}{description && (<div className="task-description"><p>{description}</p></div>)}{/* 任务元数据 */}<div className="task-meta"><span className={`priority priority-${priority}`}>{priority.toUpperCase()}</span><span className="created-date">Created: {formatDate(createdAt)}</span>{updatedAt !== createdAt && (<span className="updated-date">Updated: {formatDate(updatedAt)}</span>)}</div></div>);
}// 导出TaskItem组件
export default TaskItem;
任务表单组件(详细注释版)
// src/components/TaskForm.js
// 导入React和useState Hook
import React, { useState, useEffect } from 'react';// 定义TaskForm组件
function TaskForm({ task, onSubmit, onCancel }) {// 使用useState Hook管理表单状态const [formData, setFormData] = useState({title: '',description: '',priority: 'medium',dueDate: ''});// 当编辑任务时,更新表单数据useEffect(() => {if (task) {setFormData({title: task.title || '',description: task.description || '',priority: task.priority || 'medium',dueDate: task.dueDate || ''});}}, [task]);// 处理输入变化const handleChange = (e) => {const { name, value } = e.target;setFormData(prev => ({...prev,[name]: value}));};// 处理表单提交const handleSubmit = (e) => {e.preventDefault();// 验证表单if (!formData.title.trim()) {alert('Please enter a task title');return;}// 提交表单数据onSubmit(formData);};// 返回JSX元素return (<div className="task-form-overlay"><div className="task-form"><h2>{task ? 'Edit Task' : 'Add New Task'}</h2><form onSubmit={handleSubmit}>{/* 任务标题 */}<div className="form-group"><label htmlFor="title">Title *</label><inputtype="text"id="title"name="title"value={formData.title}onChange={handleChange}requiredplaceholder="Enter task title"/></div>{/* 任务描述 */}<div className="form-group"><label htmlFor="description">Description</label><textareaid="description"name="description"value={formData.description}onChange={handleChange}placeholder="Enter task description"rows="3"/></div>{/* 优先级 */}<div className="form-group"><label htmlFor="priority">Priority</label><selectid="priority"name="priority"value={formData.priority}onChange={handleChange}><option value="low">Low</option><option value="medium">Medium</option><option value="high">High</option></select></div>{/* 截止日期 */}<div className="form-group"><label htmlFor="dueDate">Due Date</label><inputtype="date"id="dueDate"name="dueDate"value={formData.dueDate}onChange={handleChange}/></div>{/* 表单按钮 */}<div className="form-actions"><button type="submit" className="submit-btn">{task ? 'Update Task' : 'Add Task'}</button><button type="button" onClick={onCancel} className="cancel-btn">Cancel</button></div></form></div></div>);
}// 导出TaskForm组件
export default TaskForm;
任务统计组件(详细注释版)
// src/components/TaskStats.js
// 导入React
import React from 'react';// 定义TaskStats组件
function TaskStats({ tasks }) {// 计算统计数据const totalTasks = tasks.length;const completedTasks = tasks.filter(task => task.completed).length;const pendingTasks = totalTasks - completedTasks;const highPriorityTasks = tasks.filter(task => task.priority === 'high').length;const mediumPriorityTasks = tasks.filter(task => task.priority === 'medium').length;const lowPriorityTasks = tasks.filter(task => task.priority === 'low').length;// 计算完成率const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;// 返回JSX元素return (<div className="task-stats"><h2>Task Statistics</h2><div className="stats-grid"><div className="stat-item"><h3>Total Tasks</h3><p className="stat-number">{totalTasks}</p></div><div className="stat-item"><h3>Completed</h3><p className="stat-number">{completedTasks}</p></div><div className="stat-item"><h3>Pending</h3><p className="stat-number">{pendingTasks}</p></div><div className="stat-item"><h3>Completion Rate</h3><p className="stat-number">{completionRate}%</p></div></div><div className="priority-stats"><h3>Priority Distribution</h3><div className="priority-bars"><div className="priority-bar"><span className="priority-label">High</span><div className="priority-bar-fill" style={{ width: `${totalTasks > 0 ? (highPriorityTasks / totalTasks) * 100 : 0}%` }}></div><span className="priority-count">{highPriorityTasks}</span></div><div className="priority-bar"><span className="priority-label">Medium</span><div className="priority-bar-fill" style={{ width: `${totalTasks > 0 ? (mediumPriorityTasks / totalTasks) * 100 : 0}%` }}></div><span className="priority-count">{mediumPriorityTasks}</span></div><div className="priority-bar"><span className="priority-label">Low</span><div className="priority-bar-fill" style={{ width: `${totalTasks > 0 ? (lowPriorityTasks / totalTasks) * 100 : 0}%` }}></div><span className="priority-count">{lowPriorityTasks}</span></div></div></div></div>);
}// 导出TaskStats组件
export default TaskStats;
样式文件(详细注释版)
/* src/App.css */
/* 应用主容器样式 */
.App {max-width: 1200px;margin: 0 auto;padding: 20px;font-family: Arial, sans-serif;
}/* 应用头部样式 */
.App-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 30px;padding-bottom: 20px;border-bottom: 2px solid #e9ecef;
}.App-header h1 {color: #333;margin: 0;
}.add-task-btn {background-color: #007bff;color: white;border: none;padding: 10px 20px;border-radius: 5px;cursor: pointer;font-size: 16px;
}.add-task-btn:hover {background-color: #0056b3;
}/* 任务统计样式 */
.task-stats {background-color: #f8f9fa;padding: 20px;border-radius: 8px;margin-bottom: 30px;
}.task-stats h2 {margin-top: 0;color: #333;
}.stats-grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));gap: 20px;margin-bottom: 20px;
}.stat-item {text-align: center;padding: 15px;background-color: white;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}.stat-item h3 {margin: 0 0 10px 0;color: #666;font-size: 14px;
}.stat-number {font-size: 24px;font-weight: bold;color: #007bff;margin: 0;
}.priority-stats h3 {margin-bottom: 15px;color: #333;
}.priority-bars {display: flex;flex-direction: column;gap: 10px;
}.priority-bar {display: flex;align-items: center;gap: 10px;
}.priority-label {width: 60px;font-weight: bold;color: #333;
}.priority-bar-fill {height: 20px;background-color: #007bff;border-radius: 10px;transition: width 0.3s ease;
}.priority-count {width: 30px;text-align: center;font-weight: bold;color: #333;
}/* 任务列表样式 */
.task-list {margin-bottom: 30px;
}.task-list h2 {color: #333;margin-bottom: 20px;
}.task-controls {display: flex;justify-content: space-between;align-items: center;margin-bottom: 20px;flex-wrap: wrap;gap: 15px;
}.filter-controls select,
.sort-controls select {padding: 8px;border: 1px solid #ddd;border-radius: 4px;margin-left: 10px;
}.tasks {display: flex;flex-direction: column;gap: 15px;
}/* 任务项样式 */
.task-item {background-color: white;border: 1px solid #ddd;border-radius: 8px;padding: 20px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);transition: all 0.3s ease;
}.task-item:hover {box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}.task-item.completed {opacity: 0.7;background-color: #f8f9fa;
}.task-item.completed .task-title h3 {text-decoration: line-through;color: #6c757d;
}.task-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 15px;
}.task-title {display: flex;align-items: center;gap: 10px;flex: 1;
}.task-title input[type="checkbox"] {width: 18px;height: 18px;cursor: pointer;
}.task-title h3 {margin: 0;color: #333;
}.task-actions {display: flex;gap: 10px;
}.edit-btn {background-color: #ffc107;color: #212529;border: none;padding: 8px 16px;border-radius: 4px;cursor: pointer;font-size: 14px;
}.edit-btn:hover {background-color: #e0a800;
}.delete-btn {background-color: #dc3545;color: white;border: none;padding: 8px 16px;border-radius: 4px;cursor: pointer;font-size: 14px;
}.delete-btn:hover {background-color: #c82333;
}.task-description {margin-bottom: 15px;
}.task-description p {margin: 0;color: #666;line-height: 1.5;
}.task-meta {display: flex;align-items: center;gap: 15px;font-size: 14px;color: #666;
}.priority {padding: 4px 8px;border-radius: 4px;font-weight: bold;font-size: 12px;
}.priority-high {background-color: #f8d7da;color: #721c24;
}.priority-medium {background-color: #fff3cd;color: #856404;
}.priority-low {background-color: #d1ecf1;color: #0c5460;
}.created-date,
.updated-date {font-size: 12px;color: #999;
}/* 任务表单样式 */
.task-form-overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0, 0, 0, 0.5);display: flex;justify-content: center;align-items: center;z-index: 1000;
}.task-form {background-color: white;padding: 30px;border-radius: 8px;max-width: 500px;width: 90%;max-height: 80vh;overflow-y: auto;
}.task-form h2 {margin-top: 0;color: #333;
}.form-group {margin-bottom: 20px;
}.form-group label {display: block;margin-bottom: 5px;font-weight: bold;color: #333;
}.form-group input,
.form-group textarea,
.form-group select {width: 100%;padding: 10px;border: 1px solid #ddd;border-radius: 4px;font-size: 16px;box-sizing: border-box;
}.form-group textarea {resize: vertical;min-height: 80px;
}.form-actions {display: flex;gap: 10px;justify-content: center;margin-top: 30px;
}.submit-btn {background-color: #28a745;color: white;border: none;padding: 12px 24px;border-radius: 4px;cursor: pointer;font-size: 16px;
}.submit-btn:hover {background-color: #218838;
}.cancel-btn {background-color: #6c757d;color: white;border: none;padding: 12px 24px;border-radius: 4px;cursor: pointer;font-size: 16px;
}.cancel-btn:hover {background-color: #5a6268;
}/* 清除所有任务按钮样式 */
.clear-all-btn {background-color: #dc3545;color: white;border: none;padding: 12px 24px;border-radius: 4px;cursor: pointer;font-size: 16px;margin-top: 20px;
}.clear-all-btn:hover {background-color: #c82333;
}/* 响应式设计 */
@media (max-width: 768px) {.App {padding: 10px;}.App-header {flex-direction: column;gap: 15px;text-align: center;}.task-controls {flex-direction: column;align-items: stretch;}.task-header {flex-direction: column;align-items: stretch;gap: 10px;}.task-actions {justify-content: center;}.task-meta {flex-direction: column;align-items: flex-start;gap: 5px;}
}

练习题目

基础练习

  1. useState练习
// 练习1:创建一个计数器组件,支持增加、减少、重置功能
function Counter() {// 使用useState Hook管理计数状态// 实现增加、减少、重置功能
}// 练习2:创建一个表单组件,管理用户输入
function UserForm() {// 使用useState Hook管理表单状态// 实现表单验证和提交
}
  1. useEffect练习
// 练习3:创建一个组件,在挂载时获取数据
function DataFetcher() {// 使用useEffect Hook获取数据// 处理加载状态和错误状态
}// 练习4:创建一个组件,监听窗口大小变化
function WindowSize() {// 使用useEffect Hook监听窗口大小// 显示当前窗口尺寸
}

进阶练习

  1. 自定义Hook练习
// 练习5:创建一个自定义useToggle Hook
function useToggle(initialValue = false) {// 实现切换功能// 返回当前状态和切换函数
}// 练习6:创建一个自定义useDebounce Hook
function useDebounce(value, delay) {// 实现防抖功能// 返回防抖后的值
}
  1. 综合应用练习
// 练习7:创建一个完整的博客系统
// 包含:文章列表、文章详情、文章编辑、文章删除
// 使用:useState、useEffect、自定义Hook

学习检查点

完成标准

  • 理解useState Hook的使用
  • 掌握useEffect Hook的使用
  • 学会useContext和useReducer
  • 能够创建自定义Hook
  • 完成所有练习题目

自我测试

  1. useState Hook的作用是什么?
  2. useEffect Hook的依赖数组有什么作用?
  3. 如何创建自定义Hook?
  4. useContext Hook的使用场景是什么?

扩展阅读

推荐资源

  • React Hooks官方文档
  • useState Hook
  • useEffect Hook
  • 自定义Hook

明日预告

明天我们将学习:

  • 状态管理深入
  • Redux基础
  • Context API
  • 组件通信
  • 为状态管理学习做准备
http://www.dtcms.com/a/494880.html

相关文章:

  • 地学考研专业选择学科地理、人文地理,还是GIS?不想考数学怎么选?
  • React 2025 完全指南:核心原理、实战技巧与性能优化
  • 大数据平台建站重庆网站制作团队
  • Linux CentOS 7 安装配置HAProxy完整指南:实现高可用负载均衡
  • 【小白笔记】PyTorch 和 Python 基础的这些问题
  • linux学习笔记(35)C语言连接mysql
  • 消息推送策略:如何在营销与用户体验间找到最佳平衡点
  • go资深之路笔记(九)kafka浅析
  • Java String 性能优化与内存管理:现代开发实战指南
  • 【软考备考】 NoSQL数据库有哪些,键值型、文档型、列族型、图数据库的特点与适用场景
  • 论《素数的几种筛法》
  • html静态页面怎么放在网站上原平的旅游网站怎么做的
  • 网页设计与网站建设作业公众号小程序制作步骤
  • 律师怎么做网站简单大气网站模板
  • 偏振相机在半导体制造的领域的应用
  • Uniapp微信小程序开发:EF Core 中级联删除
  • Java从入门到精通 - 集合框架(二)
  • 3proxy保姆级教程:WIN连接远端HTTPS代理
  • 大厂AI各走“开源”路
  • 室内装修效果图网站有哪些百度网盟推广是什么
  • grootN1 grootN1.5 gr00t安装方法以及使用(学习)
  • Typora(跨平台MarkDown编辑器) v1.12.2 中文绿色版
  • Unity开发抖音小游戏的震动
  • 团队作业——概要设计和数据库设计
  • 在Spring Boot开发中,HEAD、OPTIONS和 TRACE这些HTTP方法各有其特定的应用场景和实现方式
  • Flink DataStream「全分区窗口处理」mapPartition / sortPartition / aggregate / reduce
  • 网站备案号码查询大连网页设计哪家好
  • Next.js 入门指南
  • arcgis api for javascript 修改地图图层要素默认的高亮效果
  • 【论文速递】2025年第28周(Jul-06-12)(Robotics/Embodied AI/LLM)