笔者是一个有5年开发经验的前端工程师。从接触前端就一直使用 Redux,对 Redux 也算有点心得。今天分享个小 Tip 给大家 ?。
慢慢臃肿的 Redux Store
从欣赏到慢慢嫌弃
Redux 的存在让前端开发变得简单,这点毫无疑问,否则也不会有那么多人用它。在项目初建时,Redux 中只有一两个全局状态,一切看起来都很舒服。
但是,慢慢地,页面复杂起来了。页面A中有个状态在多个组件中需要复用。
一把梭,几下复制粘贴就把这个状态放到 Redux Store 中了。暗笑:Redux 真棒。
很快,页面B也有状态需要多个组件共用了。不用想,直接梭它。
页面A、页面B又来了,页面C也来了。梭梭梭....
Store 就成这样了。
好丑,好难过,不过问题不大。
在某个周一的上午,迎来了新的需求,需要页面 B 能访问并修改 pageA.A1.A1A
中的数据。
一把梭,在页面 B 中写上 state => state.pageA.A1.A1A
,再调用修改 pageA 的相关 action。虽然完美实现了需求,但是每次看到这段代码,总觉得不舒服。它们不但丑(因为数据链路长),还别扭(因为 pageB 访问了 pageA 中的数据)。
也许是我用法不对
开始反思,是否有更好的解法。于是开始重新组织数据的结构,开启平铺模式。
甚至觉得按照页面来组织 store 不优雅,应该按照领域模型来组织,将所有数据都平铺了。
不得不说,全部平铺还是很有优势的,访问是路径变短了,状态只跟领域模型有关,与页面无关了。除了命名上麻烦点。
这也是 dva 的 store 只有一层 model 的原因。但实际情况并没有变理想,多数时候一个 model 中有很多不相关的 state,比如复用后端某个接口的返回值。
找到根本原因
将状态放在 Redux Store 中,需要如下几步:
- 写好 model,将它注册到 store 中。注册到 store 的文件开始冗长,经常要解决冲突 ?。
- 访问 store 中的数据,需要不断的 state.A.B,如果没有类型文件链路长了容易出错。
- 更新某个状态调用,dispatch('updateA_B'),关键是这里的 type 是一个字符串。rematch 解决了这个问题。
想想如果不用把状态集中放到一个 store 中,而只将它存放在模块级别。访问数据时只需引入该模块暴露的 store,修改数据时只需调用该模块暴露的方法。这个模块想放哪就放哪,不用集中放在 Redux Store 目录。
尝试新的工具 use-global-hook
仓库地址:use-global-hook
基本用法
使用时只需新建一个文件存放 model,参考官网 Example。
import React from 'react';
import globalHook from 'use-global-hook';
const initialState = {
counter: 0,
};
const actions = {
addToCounter: (store, amount) => {
const newCounterValue = store.state.counter + amount;
store.setState({ counter: newCounterValue });
},
};
export const useGlobal = globalHook(React, initialState, actions);
然后在需要复用该状态的组件中,只需引用该 model 即可。import React from 'react';
import React from 'react';
import { useGlobal } from './useGlobal';
const App = () => {
const [globalState, globalActions] = useGlobal();
return (
<div>
<p>
counter:
{globalState.counter}
</p>
<button type="button" onClick={() => globalActions.addToCounter(1)}>
+1 to global
</button>
</div>
);
};
export default App;
通过 mapState 性能优化
参考:官方例子
通过 Immer 简化状态更新
参考:官方例子
通过暴露 getState 处理多个 model 存在依赖更新
没有官方例子,属于个人总结。先给每个 model 都增加一个 action,其代码如下。
function getState(store) { return store.state; }
modelB 更新依赖最新的 modelA 的值。
function useHandleAB() {
const [stateA, { updateA, getState: getStateA }] = useModelA();
const [stateB, { updateB }] = useModelB();
return function handleAB(opts) {
updateA(opts);
// 此时 stateA 已经不等于 getStateA() 了
updateB(getStateA(), opts); // 更新 modelB 依赖 modelA 的值
};
}
总结
Redux 的状态集中管理,其缺点有:
- 其 reducer 的组织方式一直有争论,原因就是集中式的管理导致 Store 变得冗长。
- 需要将所有状态进行注册,访问时又需要从 state 中取出。
- actionType 字符串的存在。
use-global-hook 通过将模型 store 存在模块级别。其模块位置可以随意放置,更灵活。省略了状态注册和取出,更简介。其 actions 也是以普通方法存在,更自然。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!