Fish Redux中的Dispatch是怎么实现的?

零.前言

我们在使用fish-redux构建应用的时候,界面代码(view)和事件的处理逻辑(reducer,effect)是完全解耦的,界面需要处理事件的时候将action分发给对应的事件处理逻辑去进行处理,而这个分发的过程就是下面要讲的dispatch, 通过本篇的内容,你可以更深刻的理解一个action是如何一步步去进行分发的。

一.从example开始

为了更好的理解action的dispatch过程,我们就先以todo_list_page中一条todo条目的勾选事件为例,来看点击后事件的传递过程,通过断点debug我们很容易就能够发现点击时候发生的一切,具体过程如下:

  1. 用户点击勾选框,GestureDetector的onTap会被回调
  2. 通过buildView传入的dispatch函数对doneAction进行分发,发现todo_component的effect中无法处理此doneAction,所以将其交给pageStore的dispatch继续进行分发
  3. pageStore的dispatch会将action交给reducer进行处理,故doneAction对应的_markDone会被执行,对state进行clone,并修改clone后的state的状态,然后将这个全新的state返回
  4. 然后pageStore的dispatch会通知所有的listeners,其中负责界面重绘的_viewUpdater发现state发生变化,通知界面进行重绘更新

二.Dispatch实现分析

Dispatch在实现的过程中借鉴了Elm。

Dispatch在fish-redux中的定义如下

typedef Dispatch = void Function(Action action);

本质上就是一个action的处理函数,接受一个action,然后对action进行分发。

下面我门通过源码来进行详细的分析

1.component中的dispatch

buildView函数传入的dispatch是对应的component的mainCtx中的dispatch,
_mainCtx和componet的关系如下
component -> ComponentWidget -> ComponentState -> _mainCtx -> _dispatch
而 _mainCtx的初始化则是通过componet的createContext方法来创建的,顺着方法下去我们看到了dispatch的初始化

// redux_component/context.dart DefaultContext初始化方法DefaultContext({@required this.factors,@required this.store,@required BuildContext buildContext,@required this.getState,})  : assert(factors != null),assert(store != null),assert(buildContext != null),assert(getState != null),_buildContext = buildContext {final OnAction onAction = factors.createHandlerOnAction(this);/// create Dispatch_dispatch = factors.createDispatch(onAction, this, store.dispatch);/// Register inter-component broadcast_onBroadcast =factors.createHandlerOnBroadcast(onAction, this, store.dispatch);registerOnDisposed(store.registerReceiver(_onBroadcast));}

context中的dispatch是通过factors来进行创建的,factors其实就是当前component,factors创建dispatch的时候传入了onAction函数,以及context自己和store的dispatch。onAction主要是进行Effect处理。
这边还可以看到,进行context初始化的最后,还将自己的onAction包装注册到store的广播中去,这样就可以接收到别人发出的action广播。

Component继承自Logic

// redux_component/logic.dart@overrideDispatch createDispatch(OnAction onAction, Context<T> ctx, Dispatch parentDispatch) {Dispatch dispatch = (Action action) {throw Exception('Dispatching while appending your effect & onError to dispatch is not allowed.');};/// attach to store.dispatchdispatch = _applyOnAction<T>(onAction, ctx)(dispatch: (Action action) => dispatch(action),getState: () => ctx.state,)(parentDispatch);return dispatch;}static Middleware<T> _applyOnAction<T>(OnAction onAction, Context<T> ctx) {return ({Dispatch dispatch, Get<T> getState}) {return (Dispatch next) {return (Action action) {final Object result = onAction?.call(action);if (result != null && result != false) {return;}//skip-lifecycle-actionsif (action.type is Lifecycle) {return;}if (!shouldBeInterruptedBeforeReducer(action)) {ctx.pageBroadcast(action);}next(action);};};};}
}

上面分发的逻辑大概可以通过上图来表示

  1. 通过onAction将action交给component对应的effect进行处理
  2. 当effect无法处理此action,且此action非lifecycle-actions,且不需中断则广播给当前Page的其余所有effects
  3. 最后就是继续将action分发给store的dispatch(parentDispatch传入的其实就是store.dispatch)

2. store中的dispatch

从store的创建代码我们可以看到store的dispatch的具体逻辑

// redux/create_store.dartfinal Dispatch dispatch = (Action action) {_throwIfNot(action != null, 'Expected the action to be non-null value.');_throwIfNot(action.type != null, 'Expected the action.type to be non-null value.');_throwIfNot(!isDispatching, 'Reducers may not dispatch actions.');try {isDispatching = true;state = reducer(state, action);} finally {isDispatching = false;}final List<_VoidCallback> _notifyListeners = listeners.toList(growable: false,);for (_VoidCallback listener in _notifyListeners) {listener();}notifyController.add(state);};

store的dispatch过程比较简单,主要就是进行reducer的调用,处理完成后通知监听者。

3.middleware

Page继承自Component,增加了middleware机制,fish-redux的redux部分本身其实就对middleware做了支持,可以通过StoreEnhancer的方式将middlewares进行组装,合并到Store的dispatch函数中。

middleware机制可以允许我们通过中间件的方式对redux的state做AOP处理,比如fish-redux自带的logMiddleware,可以对state的变化进行log,分别打印出state变化前和变化后的值。

当Page配置了middleware之后,在创建pageStore的过程中会将配置的middleware传入,传入之后会对store的dispath进行增强加工,将middleware的处理函数串联到dispatch中。

// redux_component/component.dartWidget buildPage(P param) {return wrapper(_PageWidget<T>(component: this,storeBuilder: () => createPageStore<T>(initState(param),reducer,applyMiddleware<T>(buildMiddleware(middleware)),),));}
// redux_component/page_store.dartPageStore<T> createPageStore<T>(T preloadedState, Reducer<T> reducer,[StoreEnhancer<T> enhancer]) =>_PageStore<T>(createStore(preloadedState, reducer, enhancer));
// redux/create_store.dartStore<T> createStore<T>(T preloadedState, Reducer<T> reducer,[StoreEnhancer<T> enhancer]) =>enhancer != null? enhancer(_createStore)(preloadedState, reducer): _createStore(preloadedState, reducer);

所以这里可以看到,当传入enhancer时,createStore的工作被enhancer代理了,会返回一个经过enhancer处理过的store。而PageStore创建的时候传入的是中间件的enhancer。

// redux/apply_middleware.dartStoreEnhancer<T> applyMiddleware<T>(List<Middleware<T>> middleware) {return middleware == null || middleware.isEmpty? null: (StoreCreator<T> creator) => (T initState, Reducer<T> reducer) {assert(middleware != null && middleware.isNotEmpty);final Store<T> store = creator(initState, reducer);final Dispatch initialValue = store.dispatch;store.dispatch = (Action action) {throw Exception('Dispatching while constructing your middleware is not allowed. ''Other middleware would not be applied to this dispatch.');};store.dispatch = middleware.map((Middleware<T> middleware) => middleware(dispatch: (Action action) => store.dispatch(action),getState: store.getState,)).fold(initialValue,(Dispatch previousValue,Dispatch Function(Dispatch) element) =>element(previousValue),);return store;};
}

这里的逻辑其实就是将所有的middleware的处理函数都串到store的dispatch,这样当store进行dispatch的时候所有的中间件的处理函数也会被调用。
下面为各个处理函数的执行顺序,

首先还是component中的dispatch D1 会被执行,然后传递给store的dispatch,而此时store的dispatch已经经过中间件的增强,所以会执行中间件的处理函数,最终store的原始dispatch函数D2会被执行。

三.总结

通过上面的内容,现在我们可以知道一个action是如何一步步的派送给effect,reducer去进行处理的,我们也可以通过middleware的方式去跟踪state的变化,这样的扩展性给框架本身带来无限可能。

原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519207.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

码上用它开始Flutter混合开发——FlutterBoost

开源地址: https://github.com/alibaba/flutter_boost为什么需要混合方案 具有一定规模的App通常有一套成熟通用的基础库&#xff0c;尤其是阿里系App&#xff0c;一般需要依赖很多体系内的基础库。那么使用Flutter重新从头开发App的成本和风险都较高。所以在Native App进行渐…

Linux系统常用指令总结

来源 | CSDN 博客作者 | 不撸代码闲得慌&#xff0c;责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;系统的运行级别0&#xff1a;关机1&#xff1a;单用户模式&#xff08;可以找回丢失的密码&#xff09;2&#xff1a;多用户状态没有网络服务…

像数据科学家一样思考:12步指南(下)

第三阶段-完成 一旦产品构建完成&#xff0c;你仍然需要做一些事情来使项目更加成功并使你的未来生活更轻松。那么我们如何完成数据科学项目呢&#xff1f; 10-交付产品 完成阶段的第一步是产品交付。为了创建可以交付给客户的有效产品&#xff0c;首先必须了解客户的观点。其…

基于Tablestore管理海量快递轨迹数据架构实现

快递轨迹管理 对于一个快递公司&#xff0c;在全国范围内有着大量的快递点、快递员、运输车辆以及仓储中心。而快递自产生后&#xff0c;就会在这些地点、人物之间流转。因而&#xff0c;一套完善的快递管理追踪系统是快递公司的重要管理工具&#xff1b; 用户通过平台客户端…

完了!Python黄了! 80%的程序员:痛快!你怎么看?

Python真的万能语言&#xff1f;在我的一个朋友看来&#xff0c;他坚信 Python 可以做任何事情。其实我是不服的&#xff0c;因为我在某网站看到有条评论&#xff1a;Python将要黄了&#xff01;事实究竟如何&#xff1f;这篇文章会揭开这个黑幕&#xff0c;让程序员看清现实&a…

趣谈预留实例券,一文搞懂云上省钱最新玩法

ECS近期推出了预留实例券&#xff08;Reserved Instances&#xff09;&#xff0c;简称RI&#xff0c;这东西很cool&#xff0c;今天我们聊聊这个。 首先这篇Blog不是文档&#xff0c;读完后想详细了解文档的朋友请点击 一个小故事 我来给大家讲一个故事理解云上的几种付费方…

到底什么是空指针?如何避免空指针_01

文章目录1. 场景案例2. 针对于空指针的场景&#xff0c;有哪些方式可以避免问题1. 场景案例 package com.gblfy;import org.springframework.beans.factory.annotation.Autowired;import javax.servlet.http.HttpServletRequest;/*** 理解什么是空指针*/ public class WhatIsn…

在 IntelliJ IDEA 中部署应用到服务器(Eclipse)

在之前的文章《在 Intellij IDEA 中部署 Java 应用到 阿里云 ECS》中讲解了如何将一个本地应用部署到阿里云 ECS 上去&#xff0c;有些读者反馈目前还有一些测试机器是在经典网络&#xff0c;甚至是在本地机房中&#xff0c;咨询是否可以通过 Cloud Toolkit 插件将应用部署到这…

linux所有文件打包压缩,Linux基础教程:对文件打包压缩

一、须知文件数量太多&#xff0c; 如果需要拷来拷去是不是很麻烦&#xff1f; 怎么办&#xff1f; 打包&#xff01;文件太大&#xff0c;通过网络下载、传输会不会很费时间&#xff1f; 怎么办&#xff1f; 压缩&#xff01;在Linux环境中&#xff0c;打包压缩文件的…

钉钉平台助力中国一汽疫情防控 数字化迈入新阶段

面对突如其来的疫情&#xff0c;中国一汽紧急应对、快速响应&#xff0c;携旗下一汽-大众、一汽丰田捐赠资金8100万元&#xff0c;并成立了疫情防控专项基金。同时&#xff0c;通过旗下“一汽出行”公司组织 “特别爱心车队”&#xff0c;在抗击疫情期间&#xff0c;提供安全、…

阿里靠什么支撑 EB 级计算力?

阿里妹导读&#xff1a;MaxCompute 是阿里EB级计算平台&#xff0c;经过十年磨砺&#xff0c;它成为阿里巴巴集团数据中台的计算核心和阿里云大数据的基础服务。去年MaxCompute 做了哪些工作&#xff0c;这些工作背后的原因是什么&#xff1f;大数据市场进入普惠红海的新阶段&a…

为什么说流处理即未来?

本文整理自 Flink 创始公司 Ververica 联合创始人兼 CTO - Stephan Ewen 在 Flink Forward China 2018 上的演讲《Stream Processing takes on Everything》。 这个演讲主题看似比较激进&#xff1a;流处理解决所有问题。很多人对于 Flink 可能还停留在最初的认知&#xff0c;…

别再用那些已经淘汰的技术了!2020 年 9 大顶级 Java 框架出炉!!

来源 | Patricia Neil责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;顶级Java框架#1&#xff1a;Spring顶级Java框架#2&#xff1a;Hibernate顶级Java框架#3&#xff1a;Struts顶级Java框架#4&#xff1a;Play顶级Java框架#5&#xff1a;Googl…

字符串、数组、集合在使用时出现空指针怎么办?_03

文章目录1. 寻找代码案例中出错的场景2. 案例1. 寻找代码案例中出错的场景 字符串使用equals时报空指针错误 对象数组最燃new出来了&#xff0c;但是如果没有初始化&#xff0c;一样会报空指针错误 List对象add null不报错&#xff0c;但是addAll 不能添加null&#xff0c;否则…

云原生时代来袭 下一代云数据库技术将走向何方?

全面云化的时代已经到来&#xff0c;面对一系列的新技术和挑战&#xff0c;数据库市场将面临怎样的变革&#xff1f;作为云服务提供商&#xff0c;如何帮助更多的企业级用户把握“云”潮&#xff0c;提供最高效、最具价值的数据库解决方案&#xff1f; 日前&#xff0c;在阿里…

直播连麦贾扬清,谈谈他所理解的四大 AI 落地问题 | 攻“疫”技术公开课

从机器学习模型的层次来看&#xff0c;大致经历了两次发展浪潮&#xff1a;浅层学习&#xff08;Shallow Learning&#xff09;和深度学习&#xff08;Deep Learning&#xff09;。1980年代&#xff0c;人工神经网络的反向传播算法&#xff08;也叫Back Propagation算法或者BP算…

linux区分用户的权限级别可用,Linux用户及权限管理

基本操作首选我们梳理一下 Linux 下的用户、用户组、文件权限等基本知识&#xff0c;然后后面通过一个案例来实际演示一下权限设置的一些操作。首先 Linux 系统中&#xff0c;是有用户和用户组的概念的&#xff0c;用户就是身份的象征&#xff0c;我们必须以某一个用户身份来操…

别人家的工程师:阿里巴巴工程师有了新帮手,AI可帮助修Bug

尽管工程师用代码创造了AI&#xff0c;但AI又可以对这些代码点评一番、甚至修复Bug&#xff0c;工程师和AI的关系正在变得微妙。 AI评委引热议&#xff0c;阿里巴巴表示&#xff1a;AI不会取代工程师 4月18日&#xff0c;2019阿里巴巴研发效能峰会——“83行代码挑战赛”决赛…

31岁,年薪33万:“谢谢今天裁掉我” !有底气的人生无需解释!

最近脉脉一则帖子炸锅了&#xff1a;某HR发帖称公司以按时下班为由裁员。这种情况下很多人都慌了&#xff0c;大家纷纷把“副业救国”奉为神律。可是你有没有认真的想过&#xff0c;为什么现在大家都需要副业&#xff1a;意外裁员后&#xff0c;房贷能够按时还上不至于“回收”…

linux脚本语言求累加和,Linux Shell脚本语言与数学表达式

当你理解了Shell脚本&#xff0c;每当需要时都能流畅编写时&#xff0c;那种感觉很爽的。本章中&#xff0c;我们将教你用脚本语言进行比较复杂的数学运算。让我们从斐波那契数列开始吧。斐波那契数列&#xff0c;又称黄金分割数列&#xff0c;指的是这样一个数列&#xff1a;0…