context-reducer
TypeScript icon, indicating that this package has built-in type declarations

1.0.14 • Public • Published

中文 |
(Please contribute translations!)

context-reducer

使用useContext和useReducer的一个轻量状态管理库, 仅仅 600 字节。

  • React Hooks React Hooks 用做你所有的状态管理
  • 熟悉的 API 仅仅使用了 React,没有依赖第三方库
  • ~600 bytes min+gz.
  • TypeScript 编写 推断代码更容易,易于编写 React 代码
  • 数据请求 可以接入数据请求方法
  • 请求loading 可以自动监控请求接口的loading状态, 无需手动添加
  • useImmer 支持接入useImmer
  • 按需引入 支持es6模块语法 和 treeShaking
  • 它更容易学习。 你已经知道 useContext useReducer , 只需使用它们

安装

npm install --save context-reducer

基本使用

useContextReducer.ts

import createContextReducer, { IDispatch } from 'context-reducer';

/** 初始state值 */
const stateDefault = {
  data: 0,
  req: '',
};
type IState = typeof stateDefault;

export const reducer: React.Reducer<IState, IDispatch> = (
  state,
  action
) => {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const { type, payload, meta } = action;

  switch (type) {
    case 'CHANGE_DATA': {
      return {
        ...state,
        data: payload,
      };
    } 
    // ...

    default:
      return state;
  }
};

export default createContextReducer({ reducer, stateDefault });

index.tsx

// 根目录使用Provider包裹
import React from 'react';
import { render } from 'react-dom';
import ContextReducer from './useContextReducer';
import Show from './Show';
import Home from './Home';

function App() {
  return (
      <ContextReducer.Provider>
        <Home />
        <Show />
      </ContextReducer.Provider>
  );
}

render(<App />, document.getElementById('root'));

Home.tsx

// 对值进行操作
import React from 'react';
import contextReducer from './useContextReducer';

const Home = () => {
  const { dispatch } = contextReducer.useContextReducer();

  const clickBtn = React.useCallback((type: string) => {
    switch (type) {
      case 'add':
        dispatch((s) => {
          // s即为上一个state的值
          return {
            type: 'CHANGE_DATA',
            // 操作逻辑可放到useContextReducer.ts文件中的reducer中
            payload: s.data + 1,
          };
        });
        break;

      case 'sub':
        dispatch((s) => ({
          type: 'CHANGE_DATA',
          payload: s.data - 1,
        }));
        break;

      case 'reset':
        // 不使用回调
        dispatch({
          type: 'CHANGE_DATA',
          payload: 0,
        });
        break;

      default:
        break;
    }
  }, []);

  return (
    <>
      <button onClick={() => clickBtn('sub')}>点我-</button>
      <button onClick={() => clickBtn('add')}>点我+</button>
      <button onClick={() => clickBtn('reset')}>点我归零</button>
    </>
  );
};
export default React.memo(Home);

Show.tsx

// 展示数据
import React from 'react';
import contextReducer from './useContextReducer';

const Show = () => {
  const {
    state: { data },
  } = contextReducer.useContextReducer();

  return (
    <div>
      我是data值:
      <b>{data}</b>
    </div>
  );
};

export default React.memo(Show);

如上, 即可通过dispatch修改reducer中的值, 如果你使用过useReducer或是redux就很方便理解

接口请求

1 新建一个方法包含此模块的所有请求(使用dispatch修改状态即可, 其他是正常的js语法):

fetchContainer.ts

import axios from 'axios';
import { ReactDispatchF } from 'context-reducer';

const fetchContainer = (dispatch: ReactDispatchF) => {
  /** 接口1 */
  const fetch = async (
    /** 请求参数 */
    type: string
  ) => {
    try {
      const res: any = await axios(
        'https://www.fastmock.site/mock/5ccec72a2e72fceba0799c3844ba3c0f/xs/succ',
        {
          params: {
            type,
          },
        }
      );
      // 修改状态
      dispatch((s) => {
        return {
          type: 'CHANGE_FETCH_DATA',
          payload: res.data?.data,
          // meta:,
        };
      });
    } catch (error) {
      dispatch({
        type: 'CHANGE_FETCH_DATA_E',
        payload: error,
      });
    }
  };

  /** 接口2 */
  const fetchErr = async () => {
      
      // ...

  }

  return {
    fetch,
    fetchErr,
  };
};

export default fetchContainer;

2 在配置文件中引入请求方法

useContextReducer.ts

import createContextReducer,{  IDispatch } from 'context-reducer';
+ import fetchContainer from './fetchContainer';

/** state默认值 */
const stateDefault = {
  data: 0,
+ req: '',
};
/** state默认值类型 */
type IState = typeof stateDefault;

/** reducer控制 */
export const reducer: React.Reducer<IState, IDispatch> = (
  state,
  action
) => {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const { type, payload, meta } = action;

  switch (type) {

    //...
      
+   case 'CHANGE_FETCH_DATA': {
+     return {
+       ...state,
+       req: payload,
+     };
+   }
+   case 'CHANGE_FETCH_DATA_E': {
+     return {
+       ...state,
+       req: payload.message || '请求错误',
+     };
+   }

    default:
      return state;
  }
};

// ...

export default createContextReducer({ reducer, stateDefault, 
+ fetchContainer 
});

3 在需要调用接口的文件中调用:

Home.tsx

import React from 'react';
import contextReducer from './useContextReducer'; 
 
const Home = () => {
   const { dispatch,
+    fetchUtils, allLoading 
   } = contextReducer.useContextReducer();

+  // 获取请求方法
+  const { fetch, fetchErr } = fetchUtils;

+  // 获取loading
+  const { allFetchLoading, fetchLoading, fetchErrLoading } = allLoading

  const clickBtn = React.useCallback((type: string) => {
    switch (type) { 

      //...
      
+      case 'req':
+        // 接口请求
+        fetch('1');
+        break;
+
+      case 'reqe':
+        fetchErr();
+        break;

      default:
        break;
    }
  }, []);

  return (
    <>
      // ...
+      <button onClick={() => clickBtn('req')}>请求</button>
+      <button onClick={() => clickBtn('reqe')}>请求错误接口</button>
    </>
  );
};
export default React.memo(Home);

关于allLoading

   // 此库会自动跟踪每个接口方法的状态(接口方法在useFetch.ts中), 无需手动添加
   const { allLoading } = contextReducer.useContextReducer();
   // allFetchLoading: 所有接口的请求状态
   // fetchLoading: 对应fetch接口方法的请求状态
   // fetchErrLoading: 对应fetchErr接口方法的请求状态
   const { allFetchLoading, fetchLoading, fetchErrLoading } = allLoading

选择使用useImmer

// useContextReducer.ts入口文件中
import createContextReducer, { IDispatch } from 'context-reducer';
+import { useImmerReducer, Reducer } from 'use-immer';

/** state默认值 */
const stateDefault = {
  data: 0,
};
type IState = typeof stateDefault;

- export const reducer: React.Reducer<IState, IDispatch<string>> = (
+ export const reducer: Reducer<IState, IDispatch<string>> = (
  state,
  action
) => {
  const { type, payload, meta } = action;

-  switch (type) {
-    case 'CHANGE_DATA': {
-      return {
-        ...state,
-        data: payload,
-      };
-    } 
-    // ...
-
-    default:
-      return state;
-  }

+  switch (type) {
+    case 'CHANGE_DATA': {
+      // 使用immer可以直接赋值
+      state.data = payload;
+      break;
+    }
+    // ...
+
+    default:
+      break;
+  }

};

export default createContextReducer({ reducer, stateDefault,
+ useImmerReducer 
});

API 和 TS类型

useContextReducer(useHook)

import createContextReducer from 'context-reducer';

// reducer 状态管理逻辑(见上)
// stateDefault 初始state值
// fetchContainer fetch方法集合, 可选
const ContextReducer  = createContextReducer({ reducer, stateDefault, fetchContainer });
// ContextReducer  === { Provider, useContextReducer }

<ContextReducer.Provider>

function ParentComponent() {
  return (
    <ContextReducer.Provider>
      <ChildComponent />
    </ContextReducer.Provider>
  )
}

ContextReducer.useContextReducer()

function ChildComponent() {
  // dispatch 状态管理修改
  // fetchUtils 所包含的请求方法
  // allLoading 所有的loading, 包含每个fetch的状态
  const { state, dispatch, fetchUtils, allLoading } = ContextReducer.useContextReducer()
  return <div...
}

IDispatch(TS)

// useContextReducer.ts入口文件中
import { IDispatch } from 'context-reducer';
// 声明这里的action类型
const reducer: React.Reducer<IState, IDispatch> = (
  state,
  action
) => {
    const { type, payload, meta } = action;
    // ...

// or

// 可声明action中type为string, 或者也可声明type为枚举enum类型, 都可
const reducer: React.Reducer<IState, IDispatch<string>> = (
  state,
  action
) => {
    const { type, payload, meta } = action;
    // ...

ReactDispatchF(TS)

// fetchContainer.ts接口文件中
import { ReactDispatchF } from 'context-reducer'; 

// s声明这里使用的dispatch类型, 除了自身的类型外还包含回调参数类型
const fetchContainer = (dispatch: ReactDispatchF<string>) => {
  /** 接口 */
  const fetch = async (
    /** 请求参数 */
    type: string
  ) => {
    // ...
  }

Package Sidebar

Install

npm i context-reducer

Weekly Downloads

1

Version

1.0.14

License

MIT

Unpacked Size

83.3 kB

Total Files

29

Last publish

Collaborators

  • yzyaz