使用useContext和useReducer的一个轻量状态管理库
npm install context-reducer
中文 |
(Please contribute translations!)
sh
npm install --save context-reducer
`
基本使用
##### example地址: https://github.com/yzyaz/context-reducer/tree/master/example
#### useContextReducer.ts
`js
import createContextReducer, { IDispatch } from 'context-reducer';
/* 初始state值 /
const stateDefault = {
data: 0,
req: '',
};
type IState = typeof stateDefault;
export const reducer: React.Reducer = (
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
`js
// 根目录使用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 (
);
}
render( , document.getElementById('root'));
`
#### Home.tsx
`js
// 对值进行操作
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 (
<>
>
);
};
export default React.memo(Home);
`
#### Show.tsx
`js
// 展示数据
import React from 'react';
import contextReducer from './useContextReducer';
const Show = () => {
const {
state: { data },
} = contextReducer.useContextReducer();
return (
我是data值:
{data}
);
};
export default React.memo(Show);
`
_如上, 即可通过dispatch修改reducer中的值, 如果你使用过useReducer或是redux就很方便理解_
接口请求
1 新建一个方法包含此模块的所有请求(使用dispatch修改状态即可, 其他是正常的js语法):
#### fetchContainer.ts
`js
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
`diff
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 = (
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
`diff
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 (
<>
// ...
+
+
>
);
};
export default React.memo(Home);
`
#### 关于allLoading
`js
// 此库会自动跟踪每个接口方法的状态(接口方法在useFetch.ts中), 无需手动添加
const { allLoading } = contextReducer.useContextReducer();
// allFetchLoading: 所有接口的请求状态
// fetchLoading: 对应fetch接口方法的请求状态
// fetchErrLoading: 对应fetchErr接口方法的请求状态
const { allFetchLoading, fetchLoading, fetchErrLoading } = allLoading
`
选择使用useImmer
`diff
// 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> = (
+ export const reducer: Reducer> = (
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类型
$3
`js
import createContextReducer from 'context-reducer';
// reducer 状态管理逻辑(见上)
// stateDefault 初始state值
// fetchContainer fetch方法集合, 可选
const ContextReducer = createContextReducer({ reducer, stateDefault, fetchContainer });
// ContextReducer === { Provider, useContextReducer }
`
$3
`js
function ParentComponent() {
return (
)
}
`
$3
`js
function ChildComponent() {
// dispatch 状态管理修改
// fetchUtils 所包含的请求方法
// allLoading 所有的loading, 包含每个fetch的状态
const { state, dispatch, fetchUtils, allLoading } = ContextReducer.useContextReducer()
return }
`
$3
`js
// useContextReducer.ts入口文件中
import { IDispatch } from 'context-reducer';
// 声明这里的action类型
const reducer: React.Reducer = (
state,
action
) => {
const { type, payload, meta } = action;
// ...
// or
// 可声明action中type为string, 或者也可声明type为枚举enum类型, 都可
const reducer: React.Reducer> = (
state,
action
) => {
const { type, payload, meta } = action;
// ...
`
$3
`js
// fetchContainer.ts接口文件中
import { ReactDispatchF } from 'context-reducer';
// s声明这里使用的dispatch类型, 除了自身的类型外还包含回调参数类型
const fetchContainer = (dispatch: ReactDispatchF) => {
/* 接口 /
const fetch = async (
/* 请求参数 /
type: string
) => {
// ...
}
``