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

一、官方介绍

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 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 的 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 中一样插入和使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值