从Redux学习FP设计

2020-08-04 14:18:02

在我将目光投向Redux源代码之前,我天真地认为OOP作为一种编程范例要优于FP(函数式编程)。但这是不对的。众所周知,FP致力于形成一个易于理解和清晰的工作流,而不是那些晦涩抽象的对象和关系。它更接近人类的程序性思维方式。

现在,Reaction已经有了钩子,可以在没有Redux的情况下正确处理";States";事件。Redux的需求可能正在下降,但它的代码库仍然值得学习。特别是对于那些想要在函数式编程方面启发自己的人。所以,我想向一个好榜样学习从来都不是一个坏主意,即使它已经过时了(一点也不)。

当我开始阅读Redux源代码时,我立即感受到了我熟悉的编程语言的这种陌生用法的力量。感觉就像是用火炬照亮了壁画,探索了一个古老的洞穴,发现了这个伟大的秘密。

为了更多地了解Redux从FP中获得的好处,我研究了Redux源代码并创建了它的迷你版。

从上面的图表中,很容易找到关键字:动作存储、缩减器、查看、订阅和分派。下一步就是处理好这些关键字之间的关系。

FP在将功能结合在一起方面做得很好,没有副作用。返回值是一致的,这使得程序在执行期间或执行后的返回是可预见的。

OOP建立了一个坚实的结构,它定义了数据模型应该包含的所有属性。它使得修改或配置数据模型变得很容易。

在Redux中,Reduer和中间件通常只定义一次。这意味着,我们不需要更新这些属性的能力,也不希望它们在运行时被更改。至于FP方法,它利用闭包技术,消除了暴露内部属性的可能性。加上一些非常棒的FP技术(咖喱、作曲、管道),它甚至使程序比面向对象的程序更具人类可读性。

我想说FP应该是最适合这种情况的。当然,我在这里谈论的FP远不是像Haskell那样的真正的函数式编程。但至少在Javascript中使用FP技术的想法是可以遵循的。

在Redux中,根本没有类(在早期版本中,它曾经基于Class)。它的所有核心API都返回值或函数(函数工厂)。这正是FP期望函数的行为:

为了简单地解释Redux设计,我只实现了上面API的最核心部分。由于最新版本的核心概念没有太大变化,所以我基于非常原始的Redux v1.0.1版本编写了源代码。因为我相信第一个相关的版本会是最全面的。

CreateStore定义了那些可以在组件内使用的API。它更像是二传手和吸气球。

EXPORT DEFAULT Function createStore(Reducer,Enhener){if(Enhener){Return Enhener(CreateStore)(Reducer);}let currentstate;//Redux现在通过`ensureCanMutateNextListeners()`使用一个浅拷贝`nextListeners`//以防止`Dispatch`let currentListeners=[];函数getState(){return currentstate;}//注册更改后执行的回调。Push(Listener);return()=>;{//空监听器const index=currentListeners。IndexOf(侦听器);currentListeners。Splice(index,1);};}函数调度(Action){currentstate=Reducer(currentstate,action);//状态改变,通知调用回调currentListeners。ForEach(listener=>;listener());}//调用虚拟Reducer调度初始化Redux({type:";my-mini-Redux";});return{getState,Dispatch,Subscribe};}。

//这只是一个通过对象函数mapValues(obj,fn){return映射的助手函数。密钥(Obj)。Reduce((result,key)=>;{result[key]=fn(obj[key],key);return result;},{});}导出默认函数组合Reducers(Reducers){返回函数组合(state={},action){//官方Redux使用`ick`过滤还原器。//让';的信任减少器是函数,此处返回mapValues(Reducers,(Reducer,Key)=>;Reducer(state[key],action))};}。

我个人认为applyMiddleware API是Redux最令人惊叹的部分。它提供了应用第三方插件的最佳解决方案。

在我的理解中,源代码中的FP组成符合数学的结合律。

(x∗(y∗z))=x∗y∗z。

ApplyMiddleware的使用实际上是管道的一种形式,它允许我们注入返回商店对象的增强函数。它非常类似于面向方面的编程,最典型的例子是注释/装饰器。

//组合函数//a(b(c()=>;Compose(a,b,c)函数Compose(...funcs){return函数。ReduceRight((Composed,f)=>;f(Composed));}导出默认函数applyMiddleware(...中间件){return Next=>;(Reducer,initialState)=>;{const store=Next(Reducer,initialState);let调度=存储。调度;常量中间件API={getState:store。GetState,Dispatch:Action=>;Dispatch(Action)};const chain=中间件。Map(MIDDLEWARE=>;MIDDLEWARE(MIDDLEWARE API));//通过对`Dispatchers`分别应用中间件Dispatch=Compose(...chain,store)来增强`Dispatchers`。派单);返回{...存储,派单};};}。

有一些著名的REDUX中间件,如REDUX-THUNK和[REDUX-LOGGER(https://github.com/LogRocket/redux-logger).。这些都是使用applyMiddleware API增强功能的很好的示例。此外,他们的代码库小得惊人。核心部分只有几行代码。

当我需要在代码块中使用其他上下文时,这非常有用。在上述示例中,很容易发现NEXT和ACTION作为上下文传入,以帮助处理一些复杂的情况。

Redux-thunk允许将函数用作分派参数,这样我就可以在分派之前做一些事情。

//没有redux-thunk分派({type:';action';,payload:';value';})//有redux-thunk//分派由新的函数分派(Function(Dispatch,getState){.。日志(';redux-thunk';)派单({type:';action';,payload:';value';})})。

//允许传递函数调度导出默认函数thunk({Dispatch,getState}){return Next=>;action=>;{if(typeof action=";function";){return action(Dispatch,getState);}return Next(Action);};}

//在控制台导出默认函数记录器({getState}){return Next=>;action=>;{中输出前一个和当前状态。LOG(";=Redux Logger=";);。日志(";操作类型:";,操作。Type);const premisState=getState();。Log(";prev:";,prevState);const rereturn Value=Next(Action);const nextState=getState();。LOG(";NEXT:";,nextState);。日志(";=";);return rereturn Value;};}。

我实现了迷你版的redux和一个小的计数器应用程序来演示这些功能。应用程序将执行四个算术运算:加、减、乘和除。点击操作按钮后,数字将会更改。同时,乘除法将有300ms';延迟,这是由自定义中间件实现的(一个迷你重复数据备份)。

该应用程序有一个子组件:MiniReduxComp。在我的Mini-Redux中,我没有创建上下文提供程序来触发更新。相反,我订阅了组件中的存储更改,并执行forceUpdate以响应更改。

IMPORT,{}FROM';REACT';;IMPORT STORE FROM';../STORE';EXPORT默认类扩展{ComponentDidMount(){this。取消订阅=存储。订阅(()=>;这个。ForceUpdate());}ComponentWillUnmount(){this。取消订阅此内容(&;&;)。取消订阅();}PLUS=()=>;存储。派单({type:";加";})减去=()=>;存储。派单({type:';减去';})乘以=()=>;存储。Dispatch((Dispatch,getState)=>;{setTimeout(()=>;{Dispatch({type:';Multiply';})},300)})Divide=()=>;存储。Dispatch((Dispatch,getState)=>;{setTimeout(()=>;{Dispatch({type:';Divide';})},300)})Render(){Return(加/减1{存储。GetState()。计数}+1-1乘/除2(0.3s延迟){存储。GetState()。Double}x2/2);}}。

我认为在现代Web开发中,OOP仍然是主流。然而,我们可以看到,有一些开放源码项目混合了编程范例,并交付了非常合格的框架(例如,nest.js)。多亏了反应社区,计划生育现在是发展必需品的一部分。

好了,这就是Redux钻探的全部内容。希望您也能对Redux中的FP设计有一个很好的了解。如果你觉得这篇文章很棒,请在社交网络上分享。