WHAT - 通过 react-use 源码学习 React(State 篇)

目录

  • 一、官方介绍
    • 1. Sensors
    • 2. UI
    • 3. Animations
    • 4. Side-Effects
    • 5. Lifecycles
    • 6. State
    • 7. Miscellaneous
  • 二、源码学习
    • 示例:n. xx - yy
    • State - createMemo
    • State - createReducer

一、官方介绍

Github 地址

react-use 是一个流行的 React 自定义 Hook 库,提供了一组常用的 Hook,以帮助开发者在 React 应用程序中更方便地处理常见的任务和功能。

官方将 react-use 的 Hook 分成了以下几个主要类别,以便更好地组织和查找常用的功能。每个类别涵盖了不同类型的 Hook,满足各种开发需求。以下是这些类别的详细说明:

1. Sensors

  • 功能: 主要涉及与浏览器或用户交互相关的传感器功能。
  • 示例:
    • useMouse: 获取鼠标位置。
    • useWindowSize: 获取窗口尺寸。
    • useBattery: 监控电池状态。

2. UI

  • 功能: 涉及用户界面相关的功能,如处理样式、显示和隐藏元素等。
  • 示例:
    • useClickAway: 监听点击事件以检测用户点击是否发生在组件外部。
    • useMeasure: 测量元素的大小和位置。
    • useDarkMode: 管理和检测暗模式状态。

3. Animations

  • 功能: 处理动画和过渡效果。
  • 示例:
    • useSpring: 使用 react-spring 处理动画效果。
    • useTransition: 使用 react-spring 处理过渡动画。

4. Side-Effects

  • 功能: 处理副作用相关的 Hook,包括数据获取、异步操作等。
  • 示例:
    • useAsync: 处理异步操作,如数据获取,并提供状态和结果。
    • useFetch: 简化数据获取操作。
    • useAxios: 使用 Axios 进行数据请求的 Hook。

5. Lifecycles

  • 功能: 处理组件生命周期相关的 Hook。
  • 示例:
    • useMount: 在组件挂载时执行的 Hook。
    • useUnmount: 在组件卸载时执行的 Hook。
    • useUpdate: 在组件更新时执行的 Hook。

6. State

  • 功能: 管理组件状态和相关逻辑。
  • 示例:
    • useState: 提供基本状态管理功能。
    • useReducer: 替代 useState 实现更复杂的状态逻辑。
    • useForm: 管理表单状态和验证。
    • useInput: 管理输入字段的状态。

7. Miscellaneous

  • 功能: 各种其他实用功能的 Hook,涵盖一些不容易归类到其他类别的功能。

这种分类方法使得 react-use 的 Hook 更加有组织和易于查找,帮助开发者快速找到需要的功能并有效地集成到他们的应用程序中。

二、源码学习

示例:n. xx - yy

something

使用

源码

解释

State - createMemo

factory of memoized hooks.

使用

import {createMemo} from 'react-use';const fibonacci = n => {if (n === 0) return 0;if (n === 1) return 1;return fibonacci(n - 1) + fibonacci(n - 2);
};const useMemoFibonacci = createMemo(fibonacci);const Demo = () => {const result = useMemoFibonacci(10);return (<div>fib(10) = {result}</div>);
};

源码

import { useMemo } from 'react';const createMemo =<T extends (...args: any) => any>(fn: T) =>(...args: Parameters<T>) =>useMemo<ReturnType<T>>(() => fn(...args), args);export default createMemo;

解释

createMemo 是一个自定义的函数,它利用了 React 的 useMemo Hook 来创建一个记忆化的版本的函数。这个函数可以用于优化性能,避免在组件的每次渲染中重复计算结果。下面详细解释它的实现和使用方法。

  1. 泛型定义:

    <T extends (...args: any) => any>
    
    • 这是一个泛型参数 T,它限制了 fn 必须是一个函数类型,接受任意数量的参数并返回任意类型的值。
  2. createMemo 函数:

    const createMemo =<T extends (...args: any) => any>(fn: T) =>(...args: Parameters<T>) =>useMemo<ReturnType<T>>(() => fn(...args), args);
    
    • fn: 这是传入的原始函数。
    • (...args: Parameters<T>): 这是 createMemo 返回的函数,它接受与 fn 相同的参数类型。
    • useMemo<ReturnType<T>>(() => fn(...args), args):
      • useMemo 用于记忆化计算结果,只有在 args 改变时才重新计算。
      • ReturnType<T> 表示 fn 的返回类型。
      • fn(...args) 是实际的函数调用。
      • argsuseMemo 的依赖项数组,只有当这些参数变化时,fn(...args) 才会重新计算。
  • useMemo: 这个 Hook 用于记忆化值。它接受一个计算函数和一个依赖项数组,只有在依赖项发生变化时才重新计算值。在 createMemo 中,useMemo 被用于记忆化函数的结果。

  • Parameters<T>: TypeScript 的内置类型工具,用于提取函数类型 T 的参数类型。Parameters<T> 生成一个元组类型,包含了 fn 函数的所有参数类型。

  • ReturnType<T>: TypeScript 的内置类型工具,用于提取函数类型 T 的返回类型。

示例使用

import React, { useState } from 'react';
import createMemo from './createMemo';// 一个简单的计算函数
const expensiveCalculation = (a: number, b: number) => {console.log('Calculating...');return a + b;
};// 使用 createMemo 创建一个记忆化的版本
const memoizedCalculation = createMemo(expensiveCalculation);const MyComponent = () => {const [count, setCount] = useState(0);const [num1, setNum1] = useState(1);const [num2, setNum2] = useState(2);// 使用记忆化函数const result = memoizedCalculation(num1, num2);return (<div><p>Result: {result}</p><button onClick={() => setCount(count + 1)}>Increment Count</button><button onClick={() => setNum1(num1 + 1)}>Change Num1</button><button onClick={() => setNum2(num2 + 1)}>Change Num2</button></div>);
};export default MyComponent;

在这个示例中:

  • createMemo 用于创建一个记忆化的 expensiveCalculation 函数。
  • memoizedCalculation 是记忆化后的函数,它在参数 num1num2 没有变化时不会重新计算。
  • 只有当 num1num2 发生变化时,expensiveCalculation 才会被重新调用,而 count 的变化不会触发计算,因为 memoizedCalculation 的依赖项是 num1num2

createMemo 适用于需要记忆化计算结果以优化性能的情况,特别是当计算函数的参数稳定时。

State - createReducer

factory of reducer hooks with custom middleware.

使用

import { createReducer } from 'react-use';
import logger from 'redux-logger';
import thunk from 'redux-thunk';const useThunkReducer = createReducer(thunk, logger);function reducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };case 'reset':return { count: action.payload };default:throw new Error();}
}const Demo = ({ initialCount = 1 }) => {// Action creator to increment count, wait a second and then resetconst addAndReset = React.useCallback(() => {return dispatch => {dispatch({ type: 'increment' });setTimeout(() => {dispatch({ type: 'reset', payload: initialCount });}, 1000);};}, [initialCount]);const [state, dispatch] = useThunkReducer(reducer, initialCount);return (<div><p>count: {state.count}</p><button onClick={() => dispatch(addAndReset())}>Add and reset</button><buttononClick={() => dispatch({ type: 'reset', payload: { count: initialCount }})}>Reset</button><button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></div>);
};

源码

import { MutableRefObject, useCallback, useRef, useState } from 'react';
import useUpdateEffect from '../useUpdateEffect';type Dispatch<Action> = (action: Action) => void;interface Store<Action, State> {getState: () => State;dispatch: Dispatch<Action>;
}type Middleware<Action, State> = (store: Store<Action, State>
) => (next: Dispatch<Action>) => (action: Action) => void;function composeMiddleware<Action, State>(chain: Middleware<Action, State>[]) {return (context: Store<Action, State>, dispatch: Dispatch<Action>) => {return chain.reduceRight((res, middleware) => {return middleware(context)(res);}, dispatch);};
}const createReducer = <Action, State>(...middlewares: Middleware<Action, State>[]) => {const composedMiddleware = composeMiddleware<Action, State>(middlewares);return (reducer: (state: State, action: Action) => State,initialState: State,initializer = (value: State) => value): [State, Dispatch<Action>] => {const ref = useRef(initializer(initialState));const [, setState] = useState(ref.current);const dispatch = useCallback((action) => {ref.current = reducer(ref.current, action);setState(ref.current);return action;},[reducer]);const dispatchRef: MutableRefObject<Dispatch<Action>> = useRef(composedMiddleware({getState: () => ref.current,dispatch: (...args: [Action]) => dispatchRef.current(...args),},dispatch));useUpdateEffect(() => {dispatchRef.current = composedMiddleware({getState: () => ref.current,dispatch: (...args: [Action]) => dispatchRef.current(...args),},dispatch);}, [dispatch]);return [ref.current, dispatchRef.current];};
};export default createReducer;

解释

createReducer 是一个自定义 Hook,用于创建一个支持中间件的 Redux 样式的 reducer。它结合了 React 的 useStateuseRefuseCallbackuseUpdateEffect Hook 来实现一个灵活的状态管理方案,并允许中间件的插入和应用。

以下是详细解析:

  1. composeMiddleware 函数
function composeMiddleware<Action, State>(chain: Middleware<Action, State>[]) {return (context: Store<Action, State>, dispatch: Dispatch<Action>) => {return chain.reduceRight((res, middleware) => {return middleware(context)(res);}, dispatch);};
}
  • 功能: 将多个中间件组合成一个最终的中间件。
  • 实现: composeMiddleware 函数接受一个中间件数组,并通过 reduceRight 将中间件依次应用到 dispatch 函数上,创建出一个最终的中间件函数。

composeMiddleware 是一个用于组合多个中间件的函数,这种组合是从右到左的顺序(这意味着最后一个中间件在最外层,第一个中间件在最内层)。下面是对该函数的详细解析:

  1. 入参 chain:

    • 类型: Middleware<Action, State>[]
    • 说明: 这是一个中间件数组,每个中间件都具有特定的签名,能够处理 StoreDispatch
  2. 返回函数的参数:

    • context:

      • 类型: Store<Action, State>
      • 说明: 包含 getStatedispatch 方法的对象,用于提供当前状态和 dispatch 函数。
    • dispatch:

      • 类型: Dispatch<Action>
      • 说明: 原始的 dispatch 函数,最终会被中间件函数包裹。

实现细节:

  1. chain.reduceRight:

    • 作用: 通过 reduceRight 方法从右向左依次应用中间件。
    • 初始值: dispatchreduceRight 的初始值,即最外层的 dispatch 函数。
  2. middleware(context)(res):

    • middleware(context):

      • 调用中间件函数,传入 context(包含 getStatedispatch)作为参数。
      • 这会返回一个函数,接受一个 next 参数。
    • middleware(context)(res):

      • 调用 middleware 返回的函数,传入上一个中间件的结果 res(初始值为原始的 dispatch)。
    • 这样,reduceRight 会依次将每个中间件函数应用于前一个中间件的结果,最终得到一个完整的中间件链。

执行顺序:

composeMiddleware 使用 reduceRight 将中间件从右到左组合:

  • 最后一个中间件: 最后一个中间件包裹最内层的 dispatch,即最基础的 dispatch 函数。
  • 第一个中间件: 第一个中间件包裹所有其他中间件的结果。

这种方式确保了中间件的执行顺序与其在数组中的排列顺序一致,且外层的中间件最先处理,内层的中间件最后处理。这种中间件组合模式是类似 Redux 的状态管理库中的一个核心概念,有助于增强 dispatch 函数的功能和可定制性。

有关多个中间件依次注册可以阅读 WHAT - Koa 介绍(含洋葱模型) 中介绍的洋葱模型。

  1. createReducer 函数
  • 功能: 创建一个包含中间件支持的 reducer 函数。

  • 参数:

    • middlewares: 中间件数组,用于增强 dispatch 函数的能力。
    • reducer: 一个接受状态和动作的 reducer 函数。
    • initialState: 初始状态。
    • initializer: 可选的状态初始化函数。
  • 返回值: 返回一个包含当前状态和 dispatch 函数的数组。

实现步骤:

  1. 状态管理:

    • ref 是一个持久化的状态引用,用于存储当前状态。
    • setState 用于触发组件的重新渲染。
  2. dispatch 函数:

    • dispatch 函数通过调用 reducer 更新状态,并触发 setState 来使组件重新渲染。
    • useCallback 确保 dispatch 函数不会在每次渲染时重新创建。
  3. 中间件应用:

    • dispatchRef 是一个 MutableRefObject,存储了应用了中间件的 dispatch 函数。
    • composedMiddleware 用于将中间件应用到 dispatch 函数上,创建出最终的 dispatch 函数。
    • useUpdateEffect 确保当 dispatch 函数更新时,中间件也会被重新应用。

这个自定义 Hook 提供了一种灵活的方式来管理和优化应用状态,使得中间件可以像在 Redux 中一样插入和使用。

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

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

相关文章

Qt 学习第7天:Qt核心特性

元对象系统Meta-object system 来自AI生成&#xff1a; Qt中的元对象系统&#xff08;Meta-Object System&#xff09;是Qt框架的一个核心特性&#xff0c;它为Qt提供了一种在运行时处理对象和类型信息的能力。元对象系统主要基于以下几个关键概念&#xff1a; 1. QObject&a…

Linux实现异步IO的方法:epoll,posix aio,libaio,io_uring

Linux中异步IO的实现方式大概有以下几种&#xff1a; 1. epoll 熟悉网络编程的人可能会想到select&#xff0c;poll&#xff0c;epoll这些异步IO的方式&#xff0c;但实际上这些方式叫做非阻塞IO&#xff0c;并不是实际意义上的异步IO。因此这些只能用于异步的Socket IO&…

有了豆包Marscode你还害怕不会写代码吗?

前言 随着科技的飞速发展&#xff0c;软件开发者们正面临着前所未有的挑战。编程任务变得越来越复杂&#xff0c;他们不仅需要编写和维护大量的代码&#xff0c;还要在严格保证代码质量的同时&#xff0c;提高开发效率。在这种背景下&#xff0c;一款高效且实用的辅助编码工具…

Depth anything v2环境相关问题

环境配置&#xff1a;numpy版本 2.x的版本不兼容。 因为我的torch版本较高&#xff0c;所以numpy改成一个较高的版本&#xff1a;1.26.4。可用 warning&#xff1a;xFormers not available xFormers: 一个用于推理加速的库&#xff0c;尤其是当输入尺寸增大时&#xff0c;能…

FastJson序列化驼峰-下划线转换问题踩坑记录

背景 问题描述 在MySQL数据表中&#xff0c;存在一个JSON结构的扩展字段&#xff0c;通过updateById进行更新写入操作。更新写入的同一个字段名出现了混合使用了驼峰命名和下划线命名两种格式。 ps: FastJson版本是1.2.83 问题影响 数仓同学离线统计数据时发现字段名有两种…

单链表——环形链表II

方法一 难想&#xff0c;但代码容易实现 根据第一道环形链表的题目我们可以得知快慢指针相交的节点&#xff0c;但是如果想要知道进入环形链表的第一个节点&#xff0c;我们就还需要定义一个指针从链表的头节点开始&#xff0c;与相交的节点同时行走&#xff0c;当两个节点重…

LeetCode刷题:3.无重复字符的最长子串

问题&#xff1a;首先分析问题得出需求 1.要求得到一个唯一最长子串的序列的长度。 子串&#xff1a;依据其形式是拥有一段长度的&#xff0c;所以考虑滑动窗口 唯一&#xff1a;考虑使用HashSet 需求描述&#xff1a;要求得到滑动窗口的大小&#xff0c;也就是左右指针的距离&…

milvus多个Querynode,资源消耗都打在一个节点上

milvus 查询时的原理 当读取数据时&#xff0c;MsgStream对象在以下场景中创建&#xff1a; 在 Milvus 中&#xff0c;数据必须先加载后才能读取。当代理收到数据加载请求时&#xff0c;会将请求发送给查询协调器&#xff0c;查询协调器决定如何将分片分配到不同的查询节点。…

根据两个位置的经纬度,计算其距离和方位

#include <iostream> #include <cmath>const double EARTH_RADIUS 6371000.0; // 地球半径 (单位&#xff1a;米) const double DEG_TO_RAD M_PI / 180.0;// 计算两个经纬度之间的距离 (单位&#xff1a;米) 和方位 (单位&#xff1a;度) void calculate_distanc…

NoSql数据库Redis集群

一、关系型数据库和 NoSQL 数据库 1.1 数据库主要分为两大类&#xff1a;关系型数据库与 NoSQL 数据库 关系型数据库 &#xff0c;是建立在关系模型基础上的数据库&#xff0c;其借助于集合代数等数学概念和方法来处理数据库中的数据主流的 MySQL 、 Oracle 、 MS SQL Server…

做数据爬虫工作:是否需要准备单独的IP库和爬虫库?

在数据爬虫领域&#xff0c;为了确保高效、稳定且合法地进行数据采集&#xff0c;准备单独的IP库和爬虫库成为了许多爬虫工程师的必备选择。本文将探讨为什么在进行数据爬虫工作时&#xff0c;准备单独的IP库和爬虫库是至关重要的。 一、为什么需要单独的IP库&#xff1f; 1.…

vue2-2024(2)

vue-router 1.路由&#xff08;vue的一个插件&#xff09;&#xff0c;就是一组映射关系&#xff1b; 2.key为路径&#xff0c;value可能是function或component 安装 vue-router vue3 对应vue-router 4&#xff08;npm i vue-router&#xff09; vue2 对应vue-router 3&#…

云计算实训35——镜像的迁移、镜像的创建、使用docker查看ip、端口映射、容器持久化

一、镜像的迁移 打包镜像 docker save -o 文件名称 镜像名&#xff1a;标签 #查看帮助命令[rootdocker ~]#docker --help#查看save打包用法[rootdocker ~]#docker save --help#查看原有镜像[rootdocker ~]#docker images#将镜像打包[rootdocker ~]#docker save -o centos.t…

logrotate.rsyslog文件中的postrotate --- endscript作用

在 logrotate 配置文件中&#xff0c;postrotate 和 endscript 之间的部分用于在日志轮转&#xff08;即日志文件被归档和压缩后&#xff09;执行特定的命令或脚本。这段代码在日志文件完成轮转后执行&#xff0c;通常用于确保日志记录服务正确重新加载并开始使用新的日志文件。…

Python实现图片的拼接

Python实现图片的拼接 Python中有多种方法可以实现图片拼接&#xff0c;下面是一个使用Pillow库的示例&#xff1a; 首先&#xff0c;你需要安装Pillow库&#xff1a; pip install pillow然后&#xff0c;可以使用以下代码实现图片拼接&#xff1a; from PIL import Image#…

MySQL——多表操作(四)子查询(1)带 IN 关键字的子查询

子查询是指一个查询语句嵌套在另一个查询语句内部的查询。它可以嵌套在SELECT、SELECT、INTO 语句、INSERT…INTO 等语句中。在执行查询语句时&#xff0c;首会执行子查询中的语句&#xff0c;然后将返回的结果作为外层查询的过滤条件&#xff0c;在子查询中通可以使用 IN、EXI…

【C++ 面试 - 内存管理】每日 3 题(九)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

uniapp、微信小程序车牌的录入的解决方案

结合uv-ui进行编写&#xff0c;键盘使用uv-ui的组件&#xff0c;其他由我们自己编写。 <template><div class"addCarContent"><div class"boxContent"><div class"carCodeInput" click"getIndex"><div:cl…

紧急通知:避坑花生壳,花生壳退钱!!!推荐使用cpolar

有个需求&#xff0c;需要使用内网穿透功能。 本地使用花生壳搭建还算可以。 基于Ubantu。 然后再通过远程进行了搭建。 但是&#xff0c;搭建不成功。 一直报处于离线状态。 给花生壳客服反馈了&#xff0c;对方技术人员也无法解决。 协商退钱&#xff0c;不同意。 网上…