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 来创建一个记忆化的版本的函数。这个函数可以用于优化性能,避免在组件的每次渲染中重复计算结果。下面详细解释它的实现和使用方法。
-
泛型定义:
<T extends (...args: any) => any>
- 这是一个泛型参数
T
,它限制了fn
必须是一个函数类型,接受任意数量的参数并返回任意类型的值。
- 这是一个泛型参数
-
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)
是实际的函数调用。args
是useMemo
的依赖项数组,只有当这些参数变化时,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
是记忆化后的函数,它在参数num1
和num2
没有变化时不会重新计算。- 只有当
num1
或num2
发生变化时,expensiveCalculation
才会被重新调用,而count
的变化不会触发计算,因为memoizedCalculation
的依赖项是num1
和num2
。
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 reset
const 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>
<button
onClick={() => 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 的 useState
、useRef
、useCallback
和 useUpdateEffect
Hook 来实现一个灵活的状态管理方案,并允许中间件的插入和应用。
以下是详细解析:
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
是一个用于组合多个中间件的函数,这种组合是从右到左的顺序(这意味着最后一个中间件在最外层,第一个中间件在最内层)。下面是对该函数的详细解析:
-
入参
chain
:- 类型:
Middleware<Action, State>[]
- 说明: 这是一个中间件数组,每个中间件都具有特定的签名,能够处理
Store
和Dispatch
。
- 类型:
-
返回函数的参数:
-
context
:- 类型:
Store<Action, State>
- 说明: 包含
getState
和dispatch
方法的对象,用于提供当前状态和dispatch
函数。
- 类型:
-
dispatch
:- 类型:
Dispatch<Action>
- 说明: 原始的
dispatch
函数,最终会被中间件函数包裹。
- 类型:
-
实现细节:
-
chain.reduceRight
:- 作用: 通过
reduceRight
方法从右向左依次应用中间件。 - 初始值:
dispatch
是reduceRight
的初始值,即最外层的dispatch
函数。
- 作用: 通过
-
middleware(context)(res)
:-
middleware(context)
:- 调用中间件函数,传入
context
(包含getState
和dispatch
)作为参数。 - 这会返回一个函数,接受一个
next
参数。
- 调用中间件函数,传入
-
middleware(context)(res)
:- 调用
middleware
返回的函数,传入上一个中间件的结果res
(初始值为原始的dispatch
)。
- 调用
-
这样,
reduceRight
会依次将每个中间件函数应用于前一个中间件的结果,最终得到一个完整的中间件链。
-
执行顺序:
composeMiddleware
使用 reduceRight
将中间件从右到左组合:
- 最后一个中间件: 最后一个中间件包裹最内层的
dispatch
,即最基础的dispatch
函数。 - 第一个中间件: 第一个中间件包裹所有其他中间件的结果。
这种方式确保了中间件的执行顺序与其在数组中的排列顺序一致,且外层的中间件最先处理,内层的中间件最后处理。这种中间件组合模式是类似 Redux 的状态管理库中的一个核心概念,有助于增强 dispatch
函数的功能和可定制性。
有关多个中间件依次注册可以阅读 WHAT - Koa 介绍(含洋葱模型) 中介绍的洋葱模型。
createReducer
函数
-
功能: 创建一个包含中间件支持的 reducer 函数。
-
参数:
middlewares
: 中间件数组,用于增强dispatch
函数的能力。reducer
: 一个接受状态和动作的 reducer 函数。initialState
: 初始状态。initializer
: 可选的状态初始化函数。
-
返回值: 返回一个包含当前状态和
dispatch
函数的数组。
实现步骤:
-
状态管理:
ref
是一个持久化的状态引用,用于存储当前状态。setState
用于触发组件的重新渲染。
-
dispatch
函数:dispatch
函数通过调用 reducer 更新状态,并触发setState
来使组件重新渲染。useCallback
确保dispatch
函数不会在每次渲染时重新创建。
-
中间件应用:
dispatchRef
是一个MutableRefObject
,存储了应用了中间件的dispatch
函数。composedMiddleware
用于将中间件应用到dispatch
函数上,创建出最终的dispatch
函数。useUpdateEffect
确保当dispatch
函数更新时,中间件也会被重新应用。
这个自定义 Hook 提供了一种灵活的方式来管理和优化应用状态,使得中间件可以像在 Redux 中一样插入和使用。