针对 react hooks 的新版本解决方案

一.redux维持原方案

若想要无缝使用原来的 redux,和其配套的中间件 promise,thunk,saga 等等的话

可以使用 redux-react-hook

github 链接 redux-react-hook

一个简单的使用例子:

import {useDispatch, useMappedState} from 'redux-react-hook';

export function DeleteButton({index}) {
// 类似于以前 react-redux 中的 connect 函数
const mapState = useCallback(
state => ({
canDelete: state.todos[index].canDelete,
name: state.todos[index].name,
}),
[index],
); // 获取 redux 的数据
const {canDelete, name} = useMappedState(mapState); // 获取 dispatch
const dispatch = useDispatch(); // button click handle
const deleteTodo = useCallback(
() =>
dispatch({
type: 'delete todo',
index,
}),
[index],
); return (
<button disabled={!canDelete} onClick={deleteTodo}>
Delete {name}
</button>
);
}

使用方法和以前一致

二.使用 useReducer 与 context

在 index 或 app 中提供全局的 redux 与 dispatch

function isPromise(obj) {
return (
!!obj &&
(typeof obj === "object" || typeof obj === "function") &&
typeof obj.then === "function"
);
} function wrapperDispatch(dispatch) {
// 功能和 redux-promise 相同
return function (action) {
isPromise(action.payload) ?
action.payload.then(v => {
dispatch({type: action.type, payload: v})
}).catch((error) => {
dispatch(Object.assign({}, action, {
payload: error,
error: true
}));
return Promise.reject(error);
})
:
dispatch(action);
};
} function Wrap(props) {
// 确保在 dispatch 后不会刷新APP组件
const [state, dispatch] = useReducer(reducers, ReducersValue);
console.log('render wrap')
return (<MainContext.Provider value={{state: state, dispatch: wrapperDispatch(dispatch)}}>{props.children}</MainContext.Provider>)
} function App() {
console.log('render App')
return <Wrap>
<Router>
<Switch>
<Route path="/login" component={Login} exact/>
<Route path="/" component={MainIndex}/>
</Switch>
</Router>
</Wrap>
}

具体使用:

function useDispatch() {
// 获取 dispatch
const store = useContext(MainContext);
return store.dispatch;
} function useStoreState(mapState) {
//存储 state 且判断是否需要 render
const {state:store} = useContext(MainContext); const mapStateFn = () => mapState(store); const [mappedState, setMappedState] = useState(() => mapStateFn()); const lastRenderedMappedState = useRef();
// Set the last mapped state after rendering.
useEffect(() => {
lastRenderedMappedState.current = mappedState;
}); useEffect(
() => {
console.log('useEffect ')
const checkForUpdates = () => {
const newMappedState = mapStateFn();
if (!_.isEqual(newMappedState, lastRenderedMappedState.current)) {
setMappedState(newMappedState);
}
}; checkForUpdates();
},
[store, mapState],
);
return mappedState
} // 组件内使用
const ResourceReducer = useStoreState(state => state.ResourceReducer)
const dispatch = useDispatch()

他的功能已经足够了,在使用的地方使用函数即可,很方便

但是也有一些不足的地方是在根源上的,即 context,

在同一个页面中 如果有多个使用 context 的地方

那么如果一旦dispatch ,其他的所有地方也会触发render 造成资源的浪费,小项目还好,大项目仍旧不可



(除非 react 的 context 函数添加 deps)

三.自定义解决方案

原理就是存储一个全局变量 ,通过 import 引入;

我自己写了一个例子:https://github.com/Grewer/react-hooks-store

想要基础的实现只需要 30+ 行的代码即可

class Modal {
private value: any;
private prevValue: any;
private reducers: (state, action) => {};
private queue: any = [];
private dispatch: (action) => void; constructor(reducers) {
this.reducers = combineReducers(reducers)
// combineReducers 来自于 reudx ,可以引入也可以自己写一个(后续我会写一个库,会包含此函数)
this.value = this.reducers({}, {})
this.dispatch = action => {
this.prevValue = this.value;
this.value = this.reducers(this.value, action)
this.onDataChange()
}
} useModal = (deps?: string[]) => {
const [, setState] = useState(this.value);
useEffect(() => {
const index = this.queue.push({setState, deps}); // 订阅
return () => { // 组件销毁时取消
this.queue.splice(index - 1, 1);
};
}, []);
return [this.value, this.dispatch]
} onDataChange = () => {
this.queue.forEach((queue) => {
const isRender = queue.deps ? queue.deps.some(dep => this.prevValue[dep] !== this.value[dep]) : true
isRender && queue.setState(this.value)
});
}
}

// 初始化 reducers

const modal = new Modal({
countReducer: function (state = 0, action) {
console.log('count Reducer', state, action)
switch (action.type) {
case "ADD":
console.log('trigger')
return state + action.payload || 1
default:
return state
}
},
listReducer: function (state = [] as any, action) {
console.log('list Reducer', state, action)
switch (action.type) {
case "ADD_LIST":
console.log('trigger')
state.push(action.payload)
return [...state]
default:
return state
}
},
personReducer: function (state = {name: 'lll', age: 18} as any, action) {
console.log('person Reducer', state, action)
switch (action.type) {
case "CHANGE_NAME":
return Object.assign({}, state, {name: action.payload})
default:
return state
}
}
})
// 导出 useModal
export const useModal = modal.useModal

简单的使用:

function Count(props) {
const [state, dispatch] = useModal(['countReducer'])
// 非 countReducer 的更新 不会触发此函数 render
console.warn('render Count', state, dispatch) return <div>
<button onClick={() => dispatch({type: "ADD", payload: 2})}>+</button>
</div>
}

当然你也可以自己写一个,自己想要的方案

总结

hooks 的存储方案基本就这 3 类,可以用现成的,也可以使用自己写的方案

react hooks 全面转换攻略(三) 全局存储解决方案的更多相关文章

  1. react hooks 全面转换攻略(一) react本篇之useState,useEffect

    useState 经典案例: import { useState } from 'react'; function Example() { const [count, setCount] = useS ...

  2. react hooks 全面转换攻略(二) react本篇剩余 api

    useCallback,useMemo 因为这两个 api 的作用是一样的,所以我放在一起讲; 语法: function useMemo<T>(factory: () => T, d ...

  3. 攻略三战的完美体验3Castle Fantisia阿兰·梅希亚战争艾伦西战记它包含重做版本(这是新的艾伦·梅希亚大战)

    (城堡幻想曲3,纠正大家个错误哦,不是圣魔大战3,圣魔大战是城堡幻想曲2,圣魔大战不是个系列,艾伦西亚战记==艾伦希亚战记,一个游戏日文名:タイトル キャッスルファンタジア -エレンシア戦記-リニュー ...

  4. vc6开发ActiveX并发布全攻略(三)(转)

    一.环境: windows xp sp3 Microsoft VC++ 6.0 二.制作文件 打开iexpress.exe(windows提供的一个向导式cab制作工具,位置:C:\WINDOWS\s ...

  5. mac攻略(三) -- apache站点配置

    Mac OS X 中默认有两个目录可以直接运行你的 Web 程序, 一个是系统级的 Web 根目录:/Library/WebServer/Documents/ 此根目录我们平常使用地址http://l ...

  6. JQuery攻略(三)数组与字符串

    在上两章,JQuery攻略(一) 基础知识——选择器 与 DOM 和 JQuery攻略(二) Jquery手册 我们为后面的章节打好了基础,在这一章节中,我们继续. 在这一章节中,我们记录的是JQue ...

  7. 【与软件无关】2013赤峰地区C1科目三考试攻略【绝对原创】

    期待很久的科目三,终于在开考了.传说中的全部电子评判,让习惯给考官送礼的赤峰人民无所是从.据说前几天曾经有一个驾校,考了一整天,八十多个人一个没过的. 我这个攻略是今天通过考试后的一点心得,希望能有用 ...

  8. 老李分享:《Linux Shell脚本攻略》 要点(三)

    老李分享:<Linux Shell脚本攻略> 要点(三)   1.生产任意大小的文件 [root@localhost dd_test]#[root@localhost dd_test]# ...

  9. silverlight,WPF动画终极攻略之迟来的第三章 动画整合篇(Blend 4开发)

    原文:silverlight,WPF动画终极攻略之迟来的第三章 动画整合篇(Blend 4开发) 有个问题想请教下大家,我仿了腾讯的SL版QQ,相似度95%以上.我想写成教程教大家怎么开发出来,会不会 ...

随机推荐

  1. BZOJ 2440 莫比乌斯函数+容斥+二分

    2440: [中山市选2011]完全平方数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 5473  Solved: 2679[Submit][Sta ...

  2. HDU 5667 Sequence

    指数有递推式,可以通过矩阵快速幂来求解.再用下面这公式快速幂取模即可. (C是素数) #include<cstdio> #include<cstring> #include&l ...

  3. Shiro经过Redis管理会话实现集群(转载)

    原文:http://www.myexception.cn/software-architecture-design/1815507.html Shiro通过Redis管理会话实现集群 写在前面 1.在 ...

  4. DELPHI最新的产品路线图

    1)根据众多像您一样的客户要求,我们改为一年一个重大版本及更多更新.这个计划回到一年发布周期并提供额外的2或3个包含附加功能及支持期间发布的新版操作系统的更新. 2)在 RAD Studio  10. ...

  5. 使用Python将数据插入数据库(一)

    假如我现在有一个Excel数据表格,需要将其数据插入MySQL数据库中.数据如下: 对应的Python实现代码如下: #导入需要使用到的数据模块 import pandas as pd import ...

  6. crontab使用简介

    crontab的配置文件: 前四行是用来配置crond任务运行的环境变量 第一行SHELL变量指定了系统要使用哪个shell,这里是bash 第二行PATH变量指定了系统执行命令的路径 第三行MAIL ...

  7. Manage, Administrate and Monitor GlassFish v3 from Java code usingAMX &amp; JMX

    http://kalali.me/manage-administrate-and-monitor-glassfish-v3-from-java-code-using-amx-jmx/ Manage, ...

  8. Windows——cmd findstr 字符串查找增强使用说明

    在文件中寻找字符串. 复制代码代码如下: FINDSTR [/B] [/E] [/L] [/R] [/S] [/I] [/X] [/V] [/N] [/M] [/O] [/P] [/F:file] [ ...

  9. JS应用之禁止抓屏、复制、打印

    JS应用之禁止抓屏.复制.打印项目需要禁止抓屏.复制.打印的要求,复制.打印做起来可能顺手一点网上各种各样的脚本俯首皆是.但抓屏怎么禁止?PrintScreen是一个特殊的键,它是没有keyCode的 ...

  10. PHP 画图——使用jpgraph画图

     1.要支持中文须要用到simhei.ttf和simsun.ttc这两个字体,在使用中文的时候须要使用SetFont(FF_SIMSUN,FS_BOLD)设置字体. 将须要的字体放入到项目文件夹下 ...