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基础回顾

基本用法(详细注释版)
javascript 复制代码
// 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 函数式更新

函数式更新示例(详细注释版)
javascript 复制代码
// 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 对象状态管理

对象状态示例(详细注释版)
javascript 复制代码
// 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>
        <input
          type="text"
          placeholder="Name"
          value={user.name}
          onChange={(e) => updateUser('name', e.target.value)}
        />
        <input
          type="email"
          placeholder="Email"
          value={user.email}
          onChange={(e) => updateUser('email', e.target.value)}
        />
        <input
          type="number"
          placeholder="Age"
          value={user.age}
          onChange={(e) => updateUser('age', parseInt(e.target.value) || 0)}
        />
        <label>
          <input
            type="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 数组状态管理

数组状态示例(详细注释版)
javascript 复制代码
// 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>
        <input
          type="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' }}>
              <input
                type="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基础

基本用法(详细注释版)
javascript 复制代码
// 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>
      
      <input
        type="text"
        placeholder="Enter name"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
    </div>
  );
}

// 导出组件
export default UseEffectBasic;

2.2 数据获取

数据获取示例(详细注释版)
javascript 复制代码
// 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>
        <input
          type="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 清理函数

清理函数示例(详细注释版)
javascript 复制代码
// 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 依赖数组详解

依赖数组示例(详细注释版)
javascript 复制代码
// 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>
        <input
          type="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(详细注释版)
javascript 复制代码
// 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(详细注释版)
javascript 复制代码
// 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基础(详细注释版)
javascript 复制代码
// 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>
        <input
          type="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(详细注释版)
javascript 复制代码
// 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(详细注释版)
javascript 复制代码
// 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(详细注释版)
javascript 复制代码
// 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(详细注释版)
javascript 复制代码
// 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 Hook
  const { 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>
        <input
          type="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(详细注释版)
javascript 复制代码
// 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);
      
      // 保存到localStorage
      window.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(详细注释版)
javascript 复制代码
// src/components/UseLocalStorageExample.js
// 导入React
import React from 'react';
// 导入自定义Hook
import useLocalStorage from '../hooks/useLocalStorage';

// 定义UseLocalStorageExample组件
function UseLocalStorageExample() {
  // 使用自定义useLocalStorage Hook
  const [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:
          <input
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </label>
        <button onClick={removeName}>Clear Name</button>
      </div>
      
      {/* 年龄输入 */}
      <div>
        <label>
          Age:
          <input
            type="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:
          <select
            value={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:
          <select
            value={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;

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

项目:个人任务管理器

主应用组件(详细注释版)
javascript 复制代码
// 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 && (
          <TaskForm
            task={editingTask}
            onSubmit={editingTask ? 
              (data) => updateTask(editingTask.id, data) : 
              addTask
            }
            onCancel={cancelEdit}
          />
        )}
        
        {/* 任务列表 */}
        <TaskList
          tasks={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;
任务列表组件(详细注释版)
javascript 复制代码
// 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 => (
            <TaskItem
              key={task.id}
              task={task}
              onEdit={onEdit}
              onDelete={onDelete}
              onToggle={onToggle}
            />
          ))
        )}
      </div>
    </div>
  );
}

// 导出TaskList组件
export default TaskList;
任务项组件(详细注释版)
javascript 复制代码
// 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">
          <input
            type="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;
任务表单组件(详细注释版)
javascript 复制代码
// 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>
            <input
              type="text"
              id="title"
              name="title"
              value={formData.title}
              onChange={handleChange}
              required
              placeholder="Enter task title"
            />
          </div>
          
          {/* 任务描述 */}
          <div className="form-group">
            <label htmlFor="description">Description</label>
            <textarea
              id="description"
              name="description"
              value={formData.description}
              onChange={handleChange}
              placeholder="Enter task description"
              rows="3"
            />
          </div>
          
          {/* 优先级 */}
          <div className="form-group">
            <label htmlFor="priority">Priority</label>
            <select
              id="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>
            <input
              type="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;
任务统计组件(详细注释版)
javascript 复制代码
// 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;
样式文件(详细注释版)
css 复制代码
/* 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练习
javascript 复制代码
// 练习1:创建一个计数器组件,支持增加、减少、重置功能
function Counter() {
  // 使用useState Hook管理计数状态
  // 实现增加、减少、重置功能
}

// 练习2:创建一个表单组件,管理用户输入
function UserForm() {
  // 使用useState Hook管理表单状态
  // 实现表单验证和提交
}
  1. useEffect练习
javascript 复制代码
// 练习3:创建一个组件,在挂载时获取数据
function DataFetcher() {
  // 使用useEffect Hook获取数据
  // 处理加载状态和错误状态
}

// 练习4:创建一个组件,监听窗口大小变化
function WindowSize() {
  // 使用useEffect Hook监听窗口大小
  // 显示当前窗口尺寸
}

进阶练习

  1. 自定义Hook练习
javascript 复制代码
// 练习5:创建一个自定义useToggle Hook
function useToggle(initialValue = false) {
  // 实现切换功能
  // 返回当前状态和切换函数
}

// 练习6:创建一个自定义useDebounce Hook
function useDebounce(value, delay) {
  // 实现防抖功能
  // 返回防抖后的值
}
  1. 综合应用练习
javascript 复制代码
// 练习7:创建一个完整的博客系统
// 包含:文章列表、文章详情、文章编辑、文章删除
// 使用:useState、useEffect、自定义Hook

学习检查点

完成标准

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

自我测试

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

扩展阅读

推荐资源

明日预告

明天我们将学习:

  • 状态管理深入
  • Redux基础
  • Context API
  • 组件通信
  • 为状态管理学习做准备
相关推荐
future_studio3 小时前
聊聊 Unity(小白专享、C# 小程序 之 日历、小闹钟)
前端·html
Yeats_Liao4 小时前
Go Web 编程快速入门 · 04 - 请求对象 Request:头、体与查询参数
前端·golang·iphone
立志成为大牛的小牛4 小时前
数据结构——二十六、邻接表(王道408)
开发语言·数据结构·c++·学习·程序人生
祈祷苍天赐我java之术4 小时前
Redis 数据类型与使用场景
java·开发语言·前端·redis·分布式·spring·bootstrap
草莓熊Lotso5 小时前
C++ 方向 Web 自动化测试入门指南:从概念到 Selenium 实战
前端·c++·python·selenium
Olrookie5 小时前
若依前后端分离版学习笔记(二十)——实现滑块验证码(vue3)
java·前端·笔记·后端·学习·vue·ruoyi
请你喝好果汁6415 小时前
Conda_bashrc 初始化机制学习笔记
笔记·学习·conda
533_6 小时前
[vue] dayjs 显示实时时间
前端·javascript·vue.js
maxruan6 小时前
PyTorch学习
人工智能·pytorch·python·学习