ReactHooks(二)

news2024/9/9 0:00:20

上期在这~

ReactHooks【二】

  • 一.useReducer
    • 1.1 useReducer 的语法格式
    • 1.2 定义组件的基础结构
    • 1.3 定义 useReducer 的基础结构
      • 1.3.1按需导入 useReducer 函数
      • 1.3.2定义初始数据
      • 1.3.3 定义 reducer 函数根据旧状态,进行一系列处理,最终返回新状态:
      • 1.3.4 在 Father 组件中,调用 useReducer(reducerFn, 初始状态) 函数,并得到 reducer 返回的状态
      • 1.3.5为 reducer 中的 initState 指定数据类型
    • 1.4 使用 initAction 处理初始数据
      • 1.4.1 定义名为 initAction 的处理函数
      • 1.4.2 在 Father 组件中,使用步骤1声明的 initAction 函数如下:
    • 1.5 在 Father 组件中点击按钮修改 name 的值
      • 1.5.1为了能够触发 reducer 函数的重新执行,我们需要在调用 useReducer() 后接收返回的 dispatch 函数
      • 1.5.2 接收并处理dispatch参数
    • 1.6 使用 Immer 编写更简洁的 reducer 更新逻辑
      • 1.6.1 安装Immer
      • 1.6.2 从 use-immer 中导入 useImmerReducer
      • 1.6.3 case代码块汇总不再需要return
  • 二.useContext
    • 2.1 语法格式(使用步骤)
    • 2.2 createContext 配合 useContext 使用
    • 2.3 以非侵入的方式使用 Context【封装Context】

一.useReducer

1.1 useReducer 的语法格式

useReducer 的基础语法如下:

const [state, dispatch] = useReducer(reducer, initState, initAction?)

其中:

  • reducer 是一个函数,类似于 (prevState, action) => newState。形参 prevState 表示旧状态,形参 action 表示本次的行为,返回值 newState 表示处理完毕后的新状态。
  • initState 表示初始状态,也就是默认值。
  • initAction 是进行状态初始化时候的处理函数,它是可选的,如果提供了 initAction 函数,则会把 initState 传递给 initAction 函数进行处理,initAction 的返回值会被当做初始状态。
  • 返回值 state 是状态值。dispatch 是更新 state 的方法,让他接收 action 作为参数,useReducer 只需要调用 dispatch(action) 方法传入的 action 即可更新 state。

1.2 定义组件的基础结构

定义名为 Father 的父组件如下:

import React from 'react'

// 父组件
export const Father: React.FC = () => {
  return (
    <div>
      <button>修改 name 的值</button>
      <div className="father">
        <Son1 />
        <Son2 />
      </div>
    </div>
  )
}

定义名为 Son1 和 Son2 的两个子组件如下:

// 子组件1
const Son1: React.FC = () => {
  return <div className="son1"></div>
}

// 子组件2
const Son2: React.FC = () => {
  return <div className="son2"></div>
}

在 index.css 中添加对应的样式:

.father {
  display: flex;
  justify-content: space-between;
  width: 100vw;
}

.son1 {
  background-color: orange;
  min-height: 300px;
  flex: 1;
  padding: 10px;
}

.son2 {
  background-color: lightblue;
  min-height: 300px;
  flex: 1;
  padding: 10px;
}

1.3 定义 useReducer 的基础结构

1.3.1按需导入 useReducer 函数

import React, { useReducer } from ‘react’

1.3.2定义初始数据

const defaultState = { name: ‘test’, age: 16 }

1.3.3 定义 reducer 函数根据旧状态,进行一系列处理,最终返回新状态:

const reducer = (prevState) => {
  console.log('触发了 reducer 函数')
  return prevState
}

1.3.4 在 Father 组件中,调用 useReducer(reducerFn, 初始状态) 函数,并得到 reducer 返回的状态

// 父组件
export const Father: React.FC = () => {
  // useReducer(fn, 初始数据, 对初始数据进行处理的fn)
  const [state] = useReducer(reducer, defaultState)
  console.log(state)

  return (
    <div>
      <button>修改 name 的值</button>
      <div className="father">
        <Son1 />
        <Son2 />
      </div>
    </div>
  )
}

1.3.5为 reducer 中的 initState 指定数据类型

在 Father 组件中使用 state 时,就可以出现类型的智能提示啦

// 定义状态的数据类型
type UserType = typeof defaultState

const defaultState = { name: 'test', age: 16 }

// 给 initState 指定类型为 UserType
const reducer = (prevState: UserType) => {
  console.log('触发了 reducer 函数')
  return prevState
}

1.4 使用 initAction 处理初始数据

1.4.1 定义名为 initAction 的处理函数

如果初始数据中的 age 为小数、负数、或 0 时,对 age 进行非法值的处理:

//形参:初始状态
//返回值:处理好的初始状态
const initAction = (initState: UserType) => {
  // 把 return 的对象,作为 useReducer 的初始值
  return { ...initState, age: Math.round(Math.abs(initState.age)) || 18 }
}

1.4.2 在 Father 组件中,使用步骤1声明的 initAction 函数如下:

// 父组件
export const Father: React.FC = () => {
  // useReducer(fn, 初始数据, 对初始数据进行处理的fn)
  const [state] = useReducer(reducer, defaultState, initAction)

  // 省略其它代码...
}

可以在定义 defaultState 时,为 age 提供非法值,可以看到非法值在 initAction 中被处理掉了。

1.5 在 Father 组件中点击按钮修改 name 的值

注意禁止直接修改state数据源。

注意:这种用法是错误的,因为不能直接修改 state 的值
因为存储在 useReducer 中的数据都是“不可变”的!
要想修改 useReducer 中的数据,必须触发 reducer 函数的重新计算,
根据 reducer 形参中的旧状态对象(initState),经过一系列处理,返回一个“全新的”状态对象
state.name = ‘escook’

1.5.1为了能够触发 reducer 函数的重新执行,我们需要在调用 useReducer() 后接收返回的 dispatch 函数

// Father 父组件
const [state, dispatch] = useReducer(reducer, defaultState, initAction)
在 button 按钮的点击事件处理函数中,调用 dispatch() 函数,从而触发 reducer 函数的重新计算:

// Father 父组件
const onChangeName = () => {
  dispatch()
}
点击 Father 组件中如下的 button 按钮:

<button onClick={onChangeName}>修改 name 的值</button>
会触发 reducer 函数的重新执行,并打印 reducer 中的 console.log(),代码如下:

const reducer = (prevState: UserType) => {
  console.log('触发了 reducer 函数')
  return prevState
}

1.5.2 接收并处理dispatch参数

// 1. 定义 action 的类型
type ActionType = { type: 'UPDATE_NAME'; payload: string }

// 2. 为 action 指定类型为 ActionType
const reducer = (prevState: UserType, action: ActionType) => {
  console.log('触发了 reducer 函数', action)

  // 3. 删掉之前的代码,再重复编写这段逻辑的时候,会出现 TS 的类型提示,非常 Nice
  switch (action.type) {
    case 'UPDATE_NAME':
      return { ...prevState, name: action.payload }
    default:
      return prevState
  }
}
//同时,在 Father 组件的 onChangeName 处理函数内,调用 dispatch() 时也有了类型提示:

const onChangeName = () => {
  dispatch({ type: 'UPDATE_NAME', payload: '测试' })
}

注意:在今后的开发中,正确的顺序是先定义 ActionType 的类型,再修改 reducer 中的 switch…case… 逻辑,最后在组件中调用 dispatch() 函数哦!这样能够充分利用 TS 的类型提示。

1.6 使用 Immer 编写更简洁的 reducer 更新逻辑

1.6.1 安装Immer

npm install immer use-immer -S

1.6.2 从 use-immer 中导入 useImmerReducer

// 1. 导入 useImmerReducer
import { useImmerReducer } from 'use-immer'
// 父组件
export const Father: React.FC = () => {
  // 2. 把 useReducer() 的调用替换成 useImmerReducer()
  const [state, dispatch] = useImmerReducer(reducer, defaultState, initAction)
}

1.6.3 case代码块汇总不再需要return

Immer 内部会复制并返回新对象

const reducer = (prevState: UserType, action: ActionType) => {
  console.log('触发了 reducer 函数', action)
  switch (action.type) {
    case 'UPDATE_NAME':
      // return { ...prevState, name: action.payload }
      prevState.name = action.payload
      break
    case 'INCREMENT':
      // return { ...prevState, age: prevState.age + action.payload }
      prevState.age += action.payload
      break
    default:
      return prevState
  }
} 

二.useContext

2.1 语法格式(使用步骤)

  • 全局创建 Context 对象
  • 父组件中使用 Context.Provider 提供数据
  • 子组件中使用 useContext 使用数据
import React, { useContext } from 'react'

// 全局
const MyContext = React.createContext(初始数据)

// 父组件
const Father = () => {
  return <MyContext.Provider value={{name: 'escook', age: 22}}>
    <!-- 省略其它代码 -->
  </MyContext.Provider>
}

// 子组件
const Son = () => {
  const myCtx = useContext(MyContext)
  return <div>
    <p>姓名:{myCtx.name}</p>
    <p>年龄:{MyCtx.age}</p>
  </div>
}

2.2 createContext 配合 useContext 使用

import React, { useState, useContext } from 'react'

// 声明 TS 类型
type ContextType = { count: number; setCount: React.Dispatch<React.SetStateAction<number>> }

// 1. 创建 Context 对象
const AppContext = React.createContext<ContextType>({} as ContextType)

export const LevelA: React.FC = () => {
  const [count, setCount] = useState(0)

  return (
    <div style={{ padding: 30, backgroundColor: 'lightblue', width: '50vw' }}>
      <p>count值是:{count}</p>
      <button onClick={() => setCount((prev) => prev + 1)}>+1</button>
      {/* 2. 使用 Context.Provider 向下传递数据 */}
      <AppContext.Provider value={{ count, setCount }}>
        <LevelB />
      </AppContext.Provider>
    </div>
  )
}

export const LevelB: React.FC = () => {
  return (
    <div style={{ padding: 30, backgroundColor: 'lightgreen' }}>
      <LevelC />
    </div>
  )
}

export const LevelC: React.FC = () => {
  // 3. 使用 useContext 接收数据
  const ctx = useContext(AppContext)

  return (
    <div style={{ padding: 30, backgroundColor: 'lightsalmon' }}>
      {/* 4. 使用 ctx 中的数据和方法 */}
      <p>count值是:{ctx.count}</p>
      <button onClick={() => ctx.setCount((prev) => prev + 1)}>+1</button>
      <button onClick={() => ctx.setCount(0)}>重置</button>
    </div>
  )
}

2.3 以非侵入的方式使用 Context【封装Context】

浸入的方式就是父组件中存在MyContext.Provider代码。

核心思路:每个 Context 都创建一个对应的 Wrapper 组件,在 Wrapper 组件中使用 Provider 向
children 注入数据。
注意:Wrapper组件的命名:是由Context的名称+Wrapper构成。

// 声明 TS 类型
type ContextType = { count: number; setCount: React.Dispatch<React.SetStateAction<number>> }
// 创建 Context 对象
const AppContext = React.createContext<ContextType>({} as ContextType)

// 定义独立的 Wrapper 组件,被 Wrapper 嵌套的子组件会被 Provider 注入数据
export const AppContextWrapper: React.FC<React.PropsWithChildren> = (props) => {
  // 1. 定义要共享的数据
  const [count, setCount] = useState(0)
  // 2. 使用 AppContext.Provider 向下共享数据
  return <AppContext.Provider value={{ count, setCount }}>{props.children}</AppContext.Provider>
}

App.tsx

import React from 'react'
import { AppContextWrapper, LevelA } from '@/components/use_context/01.base.tsx'

const App: React.FC = () => {
  return (
    <AppContextWrapper>
      <!-- AppContextWrapper 中嵌套使用了 LevelA 组件,形成了父子关系 -->
      <!-- LevelA 组件会被当做 children 渲染到 Wrapper 预留的插槽中 -->
      <LevelA />
    </AppContextWrapper>
  )
}

export default App

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1958096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

论文阅读:面向自动驾驶场景的多目标点云检测算法

论文地址:面向自动驾驶场景的多目标点云检测算法 概要 点云在自动驾驶系统中的三维目标检测是关键技术之一。目前主流的基于体素的无锚框检测算法通常采用复杂的二阶段修正模块,虽然在算法性能上有所提升,但往往伴随着较大的延迟。单阶段无锚框点云检测算法简化了检测流程,…

Linux安装vmware tools(vmware tools软件包来源ESXI8.0.3)

一、默认正常安装(也可以下载文章顶部资源上传linux服务器解压安装&#xff0c;免去挂载光驱的步骤) ##挂载cdrom到/mnt目录 [rootlocalhost /]# mount /dev/cdrom /mnt mount: /dev/sr0 is write-protected, mounting read-only [rootlocalhost /]# ##切换至/mnt目录 [rootlo…

CTF竞赛介绍以及刷题网址(非常详细)零基础入门到精通,收藏这一篇就够了

前言 CTF&#xff08;Capture The Flag&#xff09;中文一般译作夺旗赛&#xff0c;在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今…

数据结构【有头双向链表】

目录 实现双向链表 双向链表数据 创建双向链表 初始化双向链表创建&#xff08;哨兵位&#xff09; 尾插 打印双向链表 头插 布尔类型 尾删 头删 查询 指定位置后插入 指定位置删除数据 销毁 顺序表和链表的分析 代码 list.h list.c test.c 注意&#xff1a…

用Python打造精彩动画与视频,2.2 使用Jupyter Notebook进行编程

2.2 使用Jupyter Notebook进行编程 Jupyter Notebook是一款广泛应用于数据科学、教学和研究的开源工具。它提供了一个交互式的编程环境&#xff0c;支持代码、文本、公式和可视化内容的集成显示&#xff0c;非常适合Python编程尤其是数据分析与可视化任务。 1. 什么是Jupyter…

2026考研数学武忠祥课程视频百度网盘资源+PDF讲义(永久更新)

虽然每年大家推荐的最多的是张宇和汤家凤&#xff0c;但是我强烈推荐武忠祥老师&#xff01; 2026考研数学武忠祥课程领取&#xff1a;2026武忠祥课程&#xff08;考研数学全程&#xff09;基础强化 武忠祥老师真宝藏老师&#xff0c;他讲课不像张宇老师那样段子频出&#xf…

模拟实现c++中的string

c内置string库的相关函数&#xff1a;string - C Reference 目录 一string类构造&#xff0c;拷贝构造和析构&#xff1a; 二string内正向迭代器实现&#xff1a; 三赋值运算符重载实现&#xff1a; 四reserve&#xff0c;empty&#xff0c;clear实现&#xff1a; 五push_b…

PHP与SEO,应用curl库获取百度下拉关键词案例!

编程语言从来都是工具&#xff0c;编程逻辑思维才是最重要的&#xff0c;在限定的规则内&#xff0c;实现自己的想法&#xff0c;正如人生一样&#xff01; 不管是python还是php只要掌握了基础语法规则&#xff0c;明确了实现过程&#xff0c;都能达到想要实现的结果&#xff0…

FFmpeg实战 - 解复用解码

文章目录 前置知识音视频基础概念解复用、解码的流程分析FFMPEG有8个常用库 常见音视频格式的介绍aac格式介绍h264格式介绍flv格式介绍mp4格式介绍 FFmpeg解码解封装实战数据包和数据帧&#xff08;AVPacket/AVFrame&#xff09;AVPacket/AVFrame的引用计数问题API介绍注意事项…

P4139 上帝与集合的正确用法

无限的2 用扩展欧拉定理处理式子 X>phi(p),上面的数 语言描述一下 我们从上面处理&#xff0c;处理到大于phi(p),用定理 我们接着处理 之后我们就可以接着处理Y 即递归phi(p) 确定递归终点phi(p)1 return 0 剩余值Z,Z%phi(1)phi(1)0; // Problem: P4139 上帝与集合…

银行贷款信用评分不足?大数据帮你找回失去的“分”

在这个信息爆炸的时代&#xff0c;无论是个人还是企业&#xff0c;数据都成为了衡量信用和评估风险的重要依据。贷款、融资、求职甚至是日常消费&#xff0c;都可能因为一份好的数据报告而变得更加顺畅。那么&#xff0c;如何高效地查询自己的大数据&#xff0c;面对评分不足时…

UDP通信 单播,广播,组播

UDP通信实现 #include <sys/types.h> #include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); - 参数&#xff1a; struct sockaddr *src_addr, socklen_t *addrlen…

十日Python项目——第四日(用户中心—收货地址)

#前言&#xff1a; 在最近十天我会用Python做一个购物类项目&#xff0c;会用到DjangoMysqlRedisVue等。 今天是第四天&#xff0c;主要负责撰写用户中心部分的收货地址部分。若是有不懂大家可以先阅读我的前三篇博客以能够顺承。 若是大家基础有不懂的&#xff0c;小编前面…

C语言:字符串函数、内存函数剖析

字符串函数、内存函数剖析 一、字符串函数&#xff08;一&#xff09;求字符串长度1、strlen&#xff08;1&#xff09;库函数实现&#xff08;2&#xff09;自定义实现 &#xff08;二&#xff09;长度不受限制的字符串函数1、strcpy&#xff08;1&#xff09;库函数实现&…

宠物猫用空气净化器真的有用吗?值得买的猫用空气净化器牌子排名

作为一名6年资深铲屎官&#xff0c;每天铲猫砂盆的工作无疑是一项挑战。家中不仅弥漫着难以忍受的气味&#xff0c;而且家里的小孩和老人偶尔会因为过敏性鼻炎或结膜炎等问题感到不适。换毛季节尤其头疼&#xff0c;浮毛无处不在&#xff1a;沙发、外套、坐垫&#xff0c;甚至连…

学习008-02-04-08 Localize UI Elements(本地化UI元素)

Localize UI Elements&#xff08;本地化UI元素&#xff09; This lesson explains how to localize an XAF application. It describes how to translate UI elements into German and create a multi-language application. 本课介绍如何本地化XAF应用程序。它描述了如何将U…

【C语言】在限制定条件下数据移动

C语言 在限制定条件下数据移动 给定一个数组 nums&#xff0c;编写一个函数将所有0移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意&#xff0c;必须在不复制数组的情况下原地对数组进行操作。 作业题 解决思路及代码 要将数组中的所有 0 移动到数组的末…

数学中的连分式、无穷连根式、平方根

连分式 连分式&#xff08;continued fraction&#xff09;由和与倒数的多层嵌套构成&#xff0c;可以是有限的&#xff0c;也可以是无限的。 表达式&#xff1a;或 import mathdef fraction_to_continued_fraction(numerator, denominator, max_terms):"""计算…

WordPress原创插件:搜索引擎抓取首图seo图片

WordPress原创插件&#xff1a;搜索引擎抓取首图seo图片 插件设置 插件将在网站头部添加适当的meta标签&#xff0c;以便百度等搜索引擎抓取指定的固定图像。 插件下载 https://download.csdn.net/download/huayula/89596527

[Meachines] [Easy] Friendzone LFI+Python-OS库污染权限提升

信息收集 IP AddressOpening Ports10.10.10.123TCP:21,22,53,80,139,443,445 $ nmap -p- 10.10.10.123 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Lin…