react-store:react hooks 結合 context api 實現 redux 的數據管理功能
@cpage/react-store
@cpage/react-store 是基於 react hooks 和 context api 實現的類似的 redux 的數據管理庫。支持數據存儲,方法調用,可以在 class 組件和 function 組件中使用,支持同步和異步的方法調用。
api 介紹
api | 作用 | 適合場景 |
---|---|---|
StoreProvider | 全局用的 provider | app.js 或者單個模塊的根文件 |
StoreContext | 存儲用的 Context | function 組件和 class 組件均適用 |
useStoreHook | store 鉤子函數,包含state和dispatch | function 組件 |
useStateHook | 獲取 state 用的 hook | function 組件 |
useDispatchHook | 進行事件派發的 hook | function 組件 |
connect | 添加屬性和方法到組件中 | function/class 組件 |
備註:
- 參數 key 指的是,各個 context 導出時候對應的值,例如
export { default as user } from './user'
,那麼 key 就是 user - 如果報語法錯誤,在檢查原因後安裝 babel 相關插件,例如 @babel/preset-react;另外建議不用 @babel/preset-env,因爲 preset-env 會在運行時將 async/await 等進行轉譯;如果一定要使用,可以配置 targets 屬性,類似這樣子
"presets": [
["@babel/preset-env", {
"targets": {
"chrome": "70"
},
useBuiltIns: "usage"
}],
"@babel/preset-react"
],
1,創建 context
屬性 | 含義 |
---|---|
state | state 屬性 |
methods | 方法,支持同步和異步兩種寫法,異步寫法需要使用 async |
reducers | 支持同步方法 |
effects | 支持異步方法,需要使用 async |
// 創建 Contexts 目錄
// user.js
export default {
state: {
id: 123,
name: 'cc'
},
reducers: {
setName(state, payload){
return {
...state,
...payload
}
}
},
effects: {
async setNameAsync(dispatch, state, payload){
await new Promise(resolve=>setTimeout(resolve,1000))
dispatch({
type: 'setName',
payload
})
}
}
}
// index.js 合併 context
export { default as user } from './user'
2,配置 StoreProvider
import { StoreProvider } from '@cpage/react-store';
import * as store from './Contexts';
ReactDOM.render(
<StoreProvider store={store}>
<App />
</StoreProvider>,
mountNode);
3,使用 connect (推薦)
備註:使用 connect 綁定的方法返回一個 promise 對象
const mapState = ({user:{id, name}}) => ({
id,
name
});
const mapDispatch = ({user:{setName, setNameAsync}}) => ({
setName,
setNameAsync
})
export default connect(mapState, mapDispatch)(DemoConnect)
// 使用
const handelClick = ()=>{
props.setName({
name: 'john'
})
.then(res=>{
console.log(res)
})
.catch(err=>{
console.log(err)
})
}
4,在 function 組件中使用
import React, { useContext } from "react";
import { StoreContext, useStoreHook } from '@cpage/react-store';
export default function DemoFunc(){
const {state, dispatch} = useContext(StoreContext)
const {user:{id, name, setName, setNameAsync}} = useStoreHook()
const handleName = ()=>{
setName({
name: '同步修改'
})
}
const handleNameAsync = ()=>{
setNameAsync({
name: '異步修改'
})
}
return (
<div>
<h1>function 類型組件</h1>
<p>用戶:name--{name} id--{state.user.id}</p>
<p><button onClick={handleName}>修改用戶</button></p>
<p><button onClick={handleNameAsync}>異步修改用戶</button></p>
</div>
)
}
備註:function 組件中使用 @cpage/react-store ,有多種調用方法。
4.1,獲取 state 數據
使用 useStoreHook(推薦使用)
import React, { useContext } from "react";
import { useStoreHook } from '@cpage/react-store';
const {user:{id, name, setName, setNameAsync}} = useStoreHook()
<div>用戶:{name}</div>
使用 useContext + StoreContext
import React, { useContext } from "react";
import { StoreContext } from '@cpage/react-store';
// state 指的是所有 context 的 state
const {state, dispatch} = useContext(StoreContext)
<div>用戶:{state.user.id}</div>
使用 useStateHook,useStateHook 接受一個參數,如果不傳則返回所有 state,傳遞對應的 key 則返回對應的 state
import { useStateHook } from '@cpage/react-store';
// 獲取所有的
const states = useStateHook()
// 獲取單個的
const userState = useStateHook('user')
4.2,使用 dispatch
使用 useStoreHook(推薦使用),同步和異步的調用方式一樣,只需要傳遞參數即可
import { useStoreHook } from '@cpage/react-store';
const {user:{id, name, setName, setNameAsync}} = useStoreHook()
getUser({
name: '異步修改'
})
使用 useContext + StoreContext,如果是異步調用參數需要爲函數
import React, { useContext } from "react";
import { StoreContext } from '@cpage/react-store';
// state 指的是所有 context 的 state
const {state, dispatch} = useContext(StoreContext)
// 同步
dispatch({
key: 'user',
type: 'setName',
payload: {
name: '同步數據'
}
})
// 異步
dispatch(()=>({
key: 'user',
type: 'setNameAsync',
payload: {
name: '異步修改'
}
}))
使用 useDispatchHook,useDispatchHook 接受一個參數,如果不傳那麼在使用 dispatch 使用需要攜帶上。如果是異步調用參數需要爲函數
import { useDispatchHook } from '@cpage/react-store';
// 不帶參數 key
const dispatchs = useDispatchHook()
dispatchs({
key: 'user',
type: 'setName',
payload: {
name: '同步數據'
}
})
// 參數 key
const dispatchs = useDispatchHook('user')
dispatchs({
type: 'setName',
payload: {
name: '同步數據'
}
})
// 異步
dispatchs(()=>({
type: 'setNameAsync',
payload: {
name: '異步修改'
}
}))
5,在 class 組件中使用
在 class 組件中使用 dispatch 調用異步函數時候,this.context.dispatch 裏面的參數是函數;使用 dispatch 調用同步函數時候,this.context.dispatch 裏面的參數是函數是 json 對象。
import React from "react";
import { StoreContext } from '@cpage/react-store';
export default class DemoClass extends React.Component {
static contextType = StoreContext;
handleAuth = ()=>{
this.context.dispatch({
key: 'user',
type: 'setName',
payload: {
name: '同步數據'
}
})
}
handleAuthAsync = ()=>{
this.context.dispatch(()=>({
key: 'user',
type: 'setNameAsync',
payload: {
name: '異步數據'
}
}))
}
render() {
return <div style={{border: '1px solid #9c9c9c'}}>
<h1>class 類型組件</h1>
<p>用戶:{this.context.state.user.name}</p>
<p><button onClick={this.handleAuth}>修改用戶</button></p>
<p><button onClick={this.handleAuthAsync}>異步修改用戶</button></p>
</div>;
}
}
6,中間件
6.1,新增中間件文件
/**
* 中間件參數
* @param {Object} store store
* @param {Object} prevState 更新前的state值
* @param {Object} nextState 更新後的state值
* @param {Object} action 派發的action
*/
export default function log(store, prevState, nextState, action){
console.log('----日誌log-----')
console.log(`修改前:${JSON.stringify(prevState)}`)
console.log(`修改後:${JSON.stringify(nextState)}`)
}
6.2,使用中間件
備註:middleware 的類型爲數組
<StoreProvider store={store} middleware={[log]}>
</StoreProvider>
7,loading
// 需要引入loading中間件
import loading from '@cpage/react-store/middlewares/loading'
// 配置
<StoreProvider store={store} middleware={[loading]}>
</StoreProvider>
// 使用
const mapState = ({user:{id, name}, loading}) => ({
id,
name,
loading
});
// 一般 loading 只需要異步函數中使用
<p>loading-setNameAsync->{props.loading.user.setNameAsync ? <span>true</span> : <span>false</span>}</p>
8,使用緩存(將 model 的數據緩存到 localStorage 裏面)
import cache from '@cpage/react-store/middlewares/cache';
<StoreProvider
store={store}
middleware={[cache]}
cache={['user']}
>
</StoreProvider>
配置 cache 屬性,cache 的值爲數組,元素是 model 的名稱,如果數組爲空則不緩存數據。數據被緩存到 localStorage 裏面。
ps:如果在使用過程中遇到什麼問題/或者有改進的意見,歡迎👏在 issues 裏面交流!歡迎🌟!