react-store:react hooks 結合 context api 實現 redux 的數據管理功能

@cpage/react-store

@cpage/react-store 是基於 react hooks 和 context api 實現的類似的 redux 的數據管理庫。支持數據存儲,方法調用,可以在 class 組件和 function 組件中使用,支持同步和異步的方法調用。

GitHub 倉庫地址

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 裏面交流!歡迎🌟!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章