JavaScript基础课程二十四、React Hooks 与实战

本课是 React 开发的核心进阶课程,聚焦 React Hooks 函数式编程体系。Hooks 彻底改变了 React 开发模式,让函数组件拥有状态与副作用能力,代码更简洁、逻辑更聚合。课程从基础 useState、useEffect、useRef 到性能优化 useMemo、useCallback,再到自定义 Hooks 封装,全面覆盖企业级 React 必备技能。通过单词实战案例,将状态管理、异步请求、DOM 操作、逻辑复用串联起来,帮助建立函数式编程思维。掌握本课内容,可独立开发标准 React 项目,轻松对接接口、处理副作用、优化性能,是 React 从入门到实战的关键一课。

一、课程学习目的

  1. 深入理解 React Hooks 的设计理念与核心作用,掌握函数式组件的状态与副作用管理。

  2. 熟练掌握常用 Hooks:useStateuseEffectuseRefuseMemouseCallback

  3. 学会自定义 Hooks 封装复用逻辑,提升代码模块化与可维护性。

  4. 掌握 React 性能优化基础手段,避免无效渲染。

  5. 能够使用 Hooks 完成完整实战项目,对接异步请求与业务逻辑。

  6. 建立 React 函数式编程思维,适配企业级 React 开发标准。

二、核心知识点讲解

1. React Hooks 概述

Hooks 是 React 16.8 推出的特性,让函数式组件拥有状态、生命周期、副作用等能力,彻底替代类组件。

Hooks 只能在函数组件顶层使用,不能在循环、条件、普通函数中调用。

优势:代码更简洁、逻辑更聚合、复用性更强、易于测试。

2. 基础必备 Hooks

  • useState:组件状态管理,修改状态自动触发视图更新。

  • useEffect:副作用处理(异步请求、定时器、DOM 操作)。

  • useRef:获取 DOM 元素、存储跨渲染数据。

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

  • useCallback:缓存函数,避免子组件重复渲染。

3. 自定义 Hooks

自定义 Hooks 是以 use 开头的函数,内部可调用其他 Hooks,用于封装可复用逻辑(如请求、表单、定时器)。

作用:抽离公共逻辑、简化组件、提高可读性。

4. useEffect 依赖规则

  • 空数组 []:仅挂载、卸载时执行一次。

  • 带依赖 [a,b]:依赖变化时执行。

  • 无依赖:每次渲染都执行。

  • 必须正确处理依赖,避免死循环。

5. React 状态更新规则

状态不可直接修改,必须通过set 函数返回新值。

对象/数组需使用展开语法创建新引用。

三、示例程序(带详细注释)

示例1:useState + useEffect 异步请求

JavaScript 复制代码
import { useState, useEffect } from 'react'

function WordList() {
  // 状态管理
  const [wordList, setWordList] = useState([])
  const [loading, setLoading] = useState(false)

  // 副作用:页面加载请求数据
  useEffect(() => {
    async function getList() {
      setLoading(true)
      // 模拟请求
      const res = await new Promise(r => setTimeout(() => r([
        { en: 'book', cn: '书' },
        { en: 'pen', cn: '笔' }
      ]), 1000))
      setWordList(res)
      setLoading(false)
    }
    getList()
  }, []) // 空依赖 → 只执行一次

  return (
    <div>
      {loading && <p>加载中...</p>}
      {wordList.map((item, i) => (
        <div key={i}>{item.en} - {item.cn}</div>
      ))}
    </div>
  )
}

示例2:useRef 获取 DOM

JavaScript 复制代码
import { useRef } from 'react'

function InputFocus() {
  const inputRef = useRef(null)

  const focus = () => {
    // 操作原生 DOM
    inputRef.current.focus()
  }

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={focus}>聚焦</button>
    </div>
  )
}

示例3:自定义 Hooks(封装单词请求)

JavaScript 复制代码
// hooks/useWord.js
import { useState, useEffect } from 'react'

// 自定义 Hook
export function useWord() {
  const [wordList, setWordList] = useState([])

  useEffect(() => {
    setTimeout(() => {
      setWordList([{ en: 'react', cn: 'React' }])
    }, 800)
  }, [])

  return wordList
}

// 组件中使用
function App() {
  const wordList = useWord()
  return <div>{wordList.map(i => <div key={i.en}>{i.en}</div>)}</div>
}

示例4:useMemo 缓存计算

JavaScript 复制代码
import { useMemo } from 'react'

function totalWord(wordList) {
  console.log('计算总数')
  return wordList.length
}

function App({ wordList }) {
  // 缓存结果,只有 wordList 变化才重新计算
  const count = useMemo(() => totalWord(wordList), [wordList])

  return <div>总数:{count}</div>
}

四、掌握技巧与方法

  1. Hooks 必须在函数顶层调用,不能在 if / for / 普通函数里使用。

  2. useEffect 必须正确配置依赖,避免死循环或不执行。

  3. 状态不可变:不能直接 push / splice,必须返回新数组/对象。

  4. 公共逻辑抽离为自定义 Hooks,实现逻辑复用。

  5. useRef 可存储任何不需要触发渲染的数据。

  6. useMemo / useCallback 用于性能优化,不要过度使用。

  7. 异步请求必须放在 useEffect 或自定义 Hooks 中。

五、课后作业

基础作业

  1. 使用 useState + useEffect 实现单词列表异步加载,展示 loading 状态。

  2. 使用 useRef 获取输入框 DOM,实现按钮自动聚焦。

  3. 了解 useMemo 缓存计算结果的用法。

进阶作业

  1. 封装一个自定义 Hook,用于获取单词列表数据。

  2. 实现单词搜索功能,输入关键词实时过滤列表。

  3. 使用 useEffect 清理定时器/订阅,防止内存泄漏。

实战作业

  1. 开发 React 单词搜索系统:包含异步加载、搜索过滤、状态管理、自定义 Hooks,完整可运行。

上一课:前端框架实战(React 基础)实战作业代码

代码功能说明

本实战作业基于 Vite + React 开发单词管理系统,使用函数式组件与 JSX 语法,通过 useState 管理响应式状态(单词列表、输入框内容),实现添加、删除单词核心功能;利用 useEffect 在组件挂载时打印初始化日志;通过 Props 将单词数据传递给子组件展示,完成组件通信。页面包含输入框、操作按钮、列表渲染、空状态提示,符合 React 基础开发规范,完整覆盖 JSX、组件拆分、状态管理、事件处理、列表渲染等核心知识点,帮助快速掌握 React 基础开发流程。

注意事项

  1. 必须安装 Node.js 环境,使用 npm 安装依赖。

  2. React 使用 className 代替 class,style 需写成对象格式。

  3. 状态不可直接修改,必须使用 set 函数生成新值。

  4. 列表渲染 key 必须唯一,建议使用唯一 id,不推荐仅用 index。

  5. 组件名必须大写,文件后缀建议使用 .jsx。

  6. 运行命令:npm install → npm run dev。

  7. 禁止在 return 中直接修改状态,会导致无限渲染。

完整实战代码

项目结构

Plain 复制代码
react-word-demo/
├── index.html
├── package.json
└── src/
    ├── main.jsx
    ├── App.jsx
    ├── components/
    │   └── WordItem.jsx
    └── index.css

src/main.jsx

JavaScript 复制代码
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)

src/App.jsx

JavaScript 复制代码
import { useState, useEffect } from 'react'
import WordItem from './components/WordItem'

function App() {
  const [wordList, setWordList] = useState([
    { en: 'apple', cn: '苹果' },
    { en: 'banana', cn: '香蕉' }
  ])
  const [inputEn, setInputEn] = useState('')
  const [inputCn, setInputCn] = useState('')

  useEffect(() => {
    console.log('单词管理器已初始化')
  }, [])

  const addWord = () => {
    if (!inputEn || !inputCn) return
    setWordList([...wordList, { en: inputEn, cn: inputCn }])
    setInputEn('')
    setInputCn('')
  }

  const delWord = (index) => {
    const newList = [...wordList]
    newList.splice(index, 1)
    setWordList(newList)
  }

  return (
    <div className="app">
      <h2>React 单词管理器</h2>
      <div className="input-box">
        <input
          type="text"
          placeholder="英文"
          value={inputEn}
          onChange={(e) => setInputEn(e.target.value)}
        />
        <input
          type="text"
          placeholder="中文"
          value={inputCn}
          onChange={(e) => setInputCn(e.target.value)}
        />
        <button onClick={addWord}>添加</button>
      </div>

      <div className="list">
        {wordList.length === 0 ? (
          <p className="empty">暂无单词</p>
        ) : (
          wordList.map((item, index) => (
            <WordItem
              key={index}
              item={item}
              index={index}
              delWord={delWord}
            />
          ))
        )}
      </div>
    </div>
  )
}

export default App

src/components/WordItem.jsx

JavaScript 复制代码
export default function WordItem({ item, index, delWord }) {
  return (
    <div className="word-item">
      <span>{item.en} --- {item.cn}</span>
      <button onClick={() => delWord(index)}>删除</button>
    </div>
  )
}

src/index.css

CSS 复制代码
.app {
  max-width: 600px;
  margin: 30px auto;
  padding: 20px;
}
.input-box {
  margin: 20px 0;
}
input {
  padding: 8px;
  margin-right: 6px;
}
button {
  padding: 8px 12px;
  background: #61dafb;
  border: none;
  cursor: pointer;
  border-radius: 4px;
}
.word-item {
  padding: 10px;
  background: #f5f5f5;
  margin: 6px 0;
  display: flex;
  justify-content: space-between;
  border-radius: 4px;
}
.empty {
  color: #999;
  padding: 20px 0;
  text-align: center;
}

运行命令

Bash 复制代码
npm install
npm run dev

作业验收标准

  1. 项目可正常启动,无报错。

  2. 可添加、删除单词,视图实时更新。

  3. 空数据时展示空状态。

  4. 使用 useState、useEffect 正确。

  5. 组件拆分清晰,Props 传参正常。

  6. 代码规范、注释清晰、符合 React 基础标准。


相关推荐
周淳APP10 小时前
【React Hook全家桶】大致过一遍React Hooks
前端·javascript·react.js·前端框架·react hooks
Marshmallowc2 个月前
React性能优化:useState初始值为什么要用箭头函数?深度解析Lazy Initialization与Fiber机制
前端·react.js·性能优化·前端框架·react hooks
A3608_(韦煜粮)4 个月前
深入理解React Hooks设计哲学与实现原理:从闭包陷阱到并发模式
javascript·性能优化·react·前端开发·react hooks·并发模式·自定义hooks
漠月瑾-西安4 个月前
React 组件二次封装实践:解决自定义 Props 传递导致的 DOM 警告问题
typescript·ant design·react hooks·react组件封装
伍哥的传说6 个月前
React Device Detect 完全指南:构建响应式跨设备应用的最佳实践
react.js·前端框架·react hooks·操作系统识别·device-detect·react设备检测·浏览器检测
JerryXZR1 年前
React开发高级篇 - React Hooks以及自定义Hooks实现思路
前端·javascript·react·react hooks
lucky.麒麟2 年前
用React给XXL-JOB开发一个新皮肤(三):实现登录页和Layout骨架
ts·xxl-job·react hooks·antd
lucky.麒麟2 年前
用React给XXL-JOB开发一个新皮肤(二):目录规划和路由初始化
ts·xxl-job·react hooks·antd
xiaoyan20173 年前
react18-webchat网页聊天实例|React Hooks+Arco Design仿微信桌面端
react18 zustand·react18仿微信·react-webchat·react hooks·react+arco实例