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

学习 Hooks【Plan - June - Week 2】

一、React API

React 提供了丰富的核心 API,用于创建组件、管理状态、处理副作用、优化性能等。本文档总结 React 常用的 API 方法和组件。


1. React 核心 API

React.createElement(type, props, …children)

  • 用于创建 React 元素,JSX 会被编译成该函数调用。
  • type:标签名或组件函数
  • props:属性对象
  • children:子节点列表

React.Component

  • React 组件基类,用于创建类组件。
  • 语法:
class MyComponent extends React.Component {render() {return <div>Hello</div>;}
}

React.PureComponent

  • 类组件基类,带浅层比较的性能优化。
  • 如果 props 和 state 没有变化,不会重新渲染。

React.Fragment

  • 用于返回多个子元素的容器,不会产生额外的 DOM 节点。
  • 语法:
<React.Fragment><Child1 /><Child2 />
</React.Fragment>

或者简写为:

<><Child1 /><Child2 />
</>

2. React Hooks API

useState(initialState)

  • 声明状态变量,返回 [state, setState]

useEffect(effect, deps)

  • 用于副作用操作,类似生命周期。
  • deps 数组决定何时重新执行副作用。

useContext(Context)

  • 订阅 React Context。

useReducer(reducer, initialState)

  • 管理复杂状态,类似 Redux。

useMemo(factory, deps)

  • 缓存计算结果,避免重复计算。

useCallback(callback, deps)

  • 缓存函数引用,避免子组件不必要渲染。

useRef(initialValue)

  • 创建可变引用对象,常用于获取 DOM 或存储变量。

useImperativeHandle(ref, createHandle, deps)

  • 自定义暴露给父组件的实例值。

useLayoutEffect(effect, deps)

  • 与 useEffect 类似,但在 DOM 变更后同步触发。

3. 辅助工具函数

React.cloneElement(element, [props], […children])

  • 克隆并修改已有 React 元素。

React.isValidElement(object)

  • 判断对象是否是有效的 React 元素。

React.Children

  • 工具对象,操作 props.children
    • React.Children.map
    • React.Children.forEach
    • React.Children.count
    • React.Children.only
    • React.Children.toArray

4. 其他重要 API

React.createContext(defaultValue)

  • 创建上下文(Context)。

React.forwardRef(renderFunction)

  • 转发 ref,允许父组件访问子组件 DOM。

React.memo(Component, [areEqual])

  • 组件的性能优化,高阶组件,类似 PureComponent。

React.lazy(factory)

  • 支持代码分割,懒加载组件。

React.Suspense

  • 配合 React.lazy 使用,显示加载状态。

二、useEffect

useEffect 是 React 中管理副作用(side effects)的 Hook。副作用包括数据获取、订阅、手动 DOM 操作等。


1. 基础用法

import React, { useEffect, useState } from 'react';function Example() {const [count, setCount] = useState(0);useEffect(() => {// 每次渲染后执行副作用document.title = `你点击了 ${count}`;});return (<div><p>你点击了 {count}</p><button onClick={() => setCount(count + 1)}>点击我</button></div>);
}

2. 参数说明

useEffect(effect: () => (void | (() => void)), deps?: DependencyList)
  • effect:副作用函数,函数内部可以执行副作用代码,且可返回清理函数。
  • deps:依赖数组(可选),用于控制副作用执行的时机。

3. 依赖数组

  • 无依赖数组:副作用在每次组件渲染后执行。
  • 空依赖数组 ([]):副作用只在组件挂载(Mount)和卸载(Unmount)时执行一次。
  • 有依赖项数组:只有当依赖项发生变化时,副作用才执行。

示例:

useEffect(() => {console.log('只在挂载和卸载时运行');
}, []);useEffect(() => {console.log('count 发生变化时运行');
}, [count]);

4. 清理副作用

  • effect 函数可以返回一个清理函数,在组件卸载或依赖更新前调用。
useEffect(() => {const id = setInterval(() => {console.log('定时器运行中');}, 1000);// 返回清理函数,清除定时器return () => clearInterval(id);
}, []);

5. 副作用示例

  • 数据请求(fetch)
  • 事件监听和解绑
  • 订阅和取消订阅
  • 手动 DOM 操作
  • 设置定时器

6. 注意事项

  • 避免无限循环:确保依赖数组正确,防止副作用无限触发。
  • 同步 vs 异步useEffect 不支持直接声明为 async 函数,但可在内部调用异步函数。

示例:

useEffect(() => {async function fetchData() {const res = await fetch('/api/data');const data = await res.json();// 处理数据}fetchData();
}, []);

7. useEffect 与生命周期对应关系

生命周期方法useEffect 变体
componentDidMountuseEffect(() => {}, [])
componentDidUpdateuseEffect(() => {})
componentWillUnmountuseEffect(() => { return () => {} }, [])

8. React 18+ 特别说明

React 18 开启严格模式下,useEffect 会在开发环境中执行两次以帮助发现副作用问题。

9. 示例完整代码

import React, { useState, useEffect } from 'react';function Timer() {const [count, setCount] = useState(0);useEffect(() => {const timerId = setInterval(() => {setCount(c => c + 1);}, 1000);return () => clearInterval(timerId);}, []);return <h1>计时器:{count}</h1>;
}

三、useContext

useContext 用于在函数组件中访问 React Context 的值,简化了跨组件传递数据的过程。


1. 什么是 Context?

  • Context 提供了一种在组件树中传递数据的方法,无需通过每一级组件的 props。
  • 适用于主题、语言、认证信息等全局数据。

2. useContext 的用法

const value = useContext(MyContext);
  • MyContext 是通过 React.createContext 创建的 Context 对象。
  • useContext 返回当前 Context 的值,即最近的 <MyContext.Provider> 所提供的值。
  • 当 Provider 的 value 改变时,组件会重新渲染。

3. 创建 Context 示例

import React, { createContext, useContext } from 'react';// 创建 Context
const ThemeContext = createContext('light');function Toolbar() {return (<div><ThemedButton /></div>);
}function ThemedButton() {// 读取 Context 值const theme = useContext(ThemeContext);return <button style={{ background: theme === 'dark' ? '#333' : '#ccc' }}>按钮</button>;
}function App() {return (// 提供 Context 值<ThemeContext.Provider value="dark"><Toolbar /></ThemeContext.Provider>);
}

4. 注意事项

  • 组件必须在对应 Context 的 Provider 内部,否则使用默认值。
  • 只有当 Context 的值发生变化时,使用该 Context 的组件才会重新渲染。
  • 不要在组件内直接修改 Context 的值,应通过 Provider 传递新的值。

5. 常见使用场景

  • 主题切换(light/dark)
  • 用户认证信息
  • 国际化语言设置
  • 全局配置参数

6. 组合多个 Context

  • 可以多次调用 useContext 读取多个不同的 Context。
const theme = useContext(ThemeContext);
const user = useContext(UserContext);

7. 对比 Context.Consumer

  • useContext 更简洁,适用于函数组件。
  • 类组件中仍可用 <Context.Consumer> 读取 Context。

四、用 Reducer 和 Context 扩展状态管理

当应用变复杂,单纯用 useState 管理状态变得笨重。此时可以使用 useReducer 管理复杂状态逻辑,结合 Context 实现跨组件共享状态,替代 Redux 等库。


1. 为什么用 Reducer?

  • 多个状态相互关联,修改逻辑复杂
  • 需要明确状态变化的过程和原因(动作)
  • 状态逻辑集中,更易维护和测试

2. useReducer 简介

const [state, dispatch] = useReducer(reducer, initialState);
  • state:当前状态
  • dispatch:派发动作(action)
  • reducer:状态变更函数 (state, action) => newState

示例:计数器

function reducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:throw new Error();}
}function Counter() {const [state, dispatch] = useReducer(reducer, { count: 0 });return (<>Count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>+1</button><button onClick={() => dispatch({ type: 'decrement' })}>-1</button></>);
}

3. 结合 Context 实现跨组件状态共享

  • statedispatch 放入 Context,供多个组件访问。
  • 这样,父组件可以集中管理状态,子组件通过 useContext 访问和派发动作。
const CountContext = React.createContext();function CounterProvider({ children }) {const [state, dispatch] = useReducer(reducer, { count: 0 });return (<CountContext.Provider value={{ state, dispatch }}>{children}</CountContext.Provider>);
}function CounterDisplay() {const { state } = React.useContext(CountContext);return <div>Count: {state.count}</div>;
}function CounterButtons() {const { dispatch } = React.useContext(CountContext);return (<><button onClick={() => dispatch({ type: 'increment' })}>+1</button><button onClick={() => dispatch({ type: 'decrement' })}>-1</button></>);
}

4. 应用结构示例

function App() {return (<CounterProvider><CounterDisplay /><CounterButtons /></CounterProvider>);
}

5. 优缺点

优点缺点
明确的状态管理流程和结构代码稍显复杂,学习曲线陡峭
集中管理状态,方便维护与测试过度使用可能导致冗余
方便实现复杂状态变化和回退等功能状态共享时 Context 过度更新会导致性能问题

6. 最佳实践

  • 只对需要共享的状态使用 Context 和 Reducer
  • 将逻辑拆分成多个 reducer 和 Context(模块化)
  • 使用 React.memo、useMemo 优化性能
  • 明确 Action 类型和 Payload,写清楚 Reducer 逻辑

以下是 React 官方文档 《井字棋教程 (Tic Tac Toe Tutorial)》 的详细 Markdown 学习笔记整理:


五、React 井字棋教程

通过构建一个经典的井字棋游戏,学习 React 基础知识,包括组件设计、状态管理、事件处理、以及提升组件能力。


1. 项目介绍

  • 井字棋是一个简单的 3 x 3 网格游戏,两名玩家轮流放置 X 和 O。
  • 目标是先在水平、垂直或对角线上连成一条线。
  • 教程通过这个项目介绍 React 的核心概念。

2. 构建游戏的步骤

  1. 创建一个 Square 组件,表示棋盘中的一个格子。
  2. 创建一个 Board 组件,包含 9 个 Square
  3. 维护棋盘状态,响应用户点击。
  4. 判断胜负逻辑。
  5. 添加游戏历史记录,实现时间旅行功能。

3. 组件设计

Square 组件

  • 功能:显示一个按钮,表示一个格子。
  • 接收 props:value (X, O 或 null),onClick 点击事件处理。
function Square({ value, onClick }) {return (<button className="square" onClick={onClick}>{value}</button>);
}

Board 组件

  • 维护 9 个格子的状态。
  • 渲染 9 个 Square,传入对应的 valueonClick
function Board() {const [squares, setSquares] = React.useState(Array(9).fill(null));function handleClick(i) {const nextSquares = squares.slice();nextSquares[i] = 'X';setSquares(nextSquares);}return (<div><div className="board-row"><Square value={squares[0]} onClick={() => handleClick(0)} />{/* 其他格子 */}</div>{/* 其他行 */}</div>);
}

4. 状态提升(Lifting State Up)

  • 当需要多个组件共享状态时,将状态提升到它们最近的共同父组件。
  • 在教程中,Board 组件的状态被提升到 Game 组件管理。
  • Game 组件管理历史状态,实现回退功能。

5. 计算游戏胜负

  • 实现一个函数 calculateWinner(squares),判断当前棋盘是否有玩家获胜。
  • 如果获胜,显示胜者信息,游戏结束。
function calculateWinner(squares) {const lines = [[0,1,2], [3,4,5], [6,7,8],[0,3,6], [1,4,7], [2,5,8],[0,4,8], [2,4,6]];for (let line of lines) {const [a,b,c] = line;if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {return squares[a];}}return null;
}

6. 处理用户交互

  • 点击格子时更新状态,切换玩家。
  • 禁止点击已被占用的格子。
  • 游戏结束后禁止继续点击。

7. 时间旅行(历史记录)

  • Game 组件维护棋盘的历史数组。
  • 用户可点击历史按钮,回到之前的任一步骤。
  • 利用数组和状态管理,实现“时间旅行”效果。
function Game() {const [history, setHistory] = React.useState([Array(9).fill(null)]);const [stepNumber, setStepNumber] = React.useState(0);const [xIsNext, setXIsNext] = React.useState(true);function handleClick(i) {const historyUpToStep = history.slice(0, stepNumber + 1);const current = historyUpToStep[historyUpToStep.length - 1];const squares = current.slice();if (calculateWinner(squares) || squares[i]) return;squares[i] = xIsNext ? 'X' : 'O';setHistory([...historyUpToStep, squares]);setStepNumber(historyUpToStep.length);setXIsNext(!xIsNext);}function jumpTo(step) {setStepNumber(step);setXIsNext((step % 2) === 0);}// 渲染历史按钮,调用 jumpTo
}

学习资料来源

React 参考
useEffect
useContext
使用 Reducer 和 Context
教程:井字棋游戏

http://www.xdnf.cn/news/948853.html

相关文章:

  • 华为云上的K8S怎么使用对象存储配置pod文件持久化。
  • Ubuntu 20.04 联网设置指南
  • python读取SQLite表个并生成pdf文件
  • mac 安装homebrew (nvm 及git)
  • 机器学习×第五卷:线性回归入门——她不再模仿,而开始试着理解你
  • 阿里云服务状态监控:实时掌握云服务健康状况
  • 八股文——JVM
  • LabVIEW超声频率跟踪
  • 积分商城小程序分销裂变系统框架设计
  • LLM - LlamaFactory 的大模型推理 踩坑记录
  • 算法思想之广度优先搜索(BFS)及示例(亲子游戏)
  • 云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
  • 安卓贝利自动点击器高级版下载安装教程
  • Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
  • SSRF由浅入深
  • 【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
  • SAP Fiori UI5 开发环境搭建和部署(含增强开发)
  • 从零手写Java版本的LSM Tree (一):LSM Tree 概述
  • XXL-JOB——源码分析解读(2)
  • 什么是VR全景技术
  • 【JMeter】接口断言
  • 在WSL2的Ubuntu镜像中安装Docker
  • claude3.7高阶玩法,生成系统架构图,国内直接使用
  • CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?
  • Linux信号保存与处理机制详解
  • 自然语言处理——循环神经网络
  • PKIX path building failed问题小结
  • Element-Plus:popconfirm与tooltip一起使用不生效?
  • Spring数据访问模块设计
  • Python自然语言处理库之gensim使用详解