npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

context-reducer

v1.0.14

Published

使用useContext和useReducer的一个轻量状态管理库

Downloads

39

Readme

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

基本使用

example地址: https://github.com/yzyaz/context-reducer/tree/master/example

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
  ) => {
    // ...
  }