“我不能创造,我不懂。”
理查德·费曼
回来的很简单。你有行动的创造者、行动、还原剂和商店。没有这么简单的是如何把所有在一起最好的或最“正确”的方式。在这个博客中,我们首先解释背后的动机使用回来的和强调它的好处,比如可以预测的状态管理和改进应用程序的性能。然后深入探究了回家的核心概念,包括操作、还原剂,和商店,提供一个逐步指南如何在JavaScript应用程序中实现回家的。我们将强调理解的重要性回来的的基本原则和展示了代码示例来演示其用法。
重写回来的,我们使用一个美妙的文章由林克拉克作为参考点,以及回来的代码库本身 当然,回来的文档 。
您可能会注意到我们使用传统pre-ES6 Javascript在本文中。这是因为每个人都谁知道Javascript知道pre-ES6 JS,,我们要确保我们不失去任何人,因为语法不熟悉。
这家商店
回来的,与任何数据层是相同的,从一个地方来存储信息。回来的,根据定义的第一原则 回来的,是一个单一的共享数据存储 ,被它的文档描述为一个“单一来源的真相”,所以我们会通过使存储单:
var商店;getInstance()函数{如果(商店)存储= createStore ();返回存储;}函数createStore(){返回{};}模块。出口= getInstance ();
分配器
第二原则是存储的状态只能改变一种方式:通过调度操作。让我们继续,写一个调度员。
然而,为了在这个调度程序更新状态,我们需要有 国家首先,让我们创建一个简单对象,其中包含当前状态。
函数createStore () {var现状后= {};}
派遣一个动作,我们需要一个减速器来调度。现在让我们创建一个默认的一个。减速机接收当前状态和一个动作,然后返回一个新版本的国家基于行动要求:
函数createStore () {var现状后= {};var currentReducer =函数(状态、动作){返回状态;}}
这是一个默认的函数来防止应用程序崩溃,直到我们正式分配还原剂,所以我们要继续,只是返回状态。基本上是一个“等待”。
商店会需要一种方法来通知感兴趣的一个更新已经派遣,所以让我们创建一个数组来房子用户:
函数createStore () {var现状后= {};var currentReducer =函数(状态、动作){返回状态;}var用户= [];}
太酷了!好,现在我们终于可以把调度员在一起。正如我们上面所说的,行动 一起交给还原剂状态 ,我们得到一个新状态 从减速机。之前如果我们想保留原始状态的变化进行比较的目的,它可能是有意义的临时存储。
因为一个行动 是派出 ,我们可以安全地假定参数调度程序接收是一种行动。
函数createStore () {var现状后= {};var currentReducer =函数(状态、动作){返回状态;}var用户= [];函数调度(行动){var prevState =现状后;}{返回调度:调度};}
我们还必须公开调度
函数时它实际上可以使用存储是进口的。很重要的。
所以,我们已经创建了一个参考旧的状态。我们现在有一个选择:我们可以把它还原剂复制状态并返回它,或者我们可以为他们做。自收到改变当前状态的副本是回来的哲学基础的一部分,我们要继续,只是手还原剂副本。
函数createStore () {var现状后= {};var currentReducer =函数(状态、动作){返回状态;}var用户= [];函数调度(行动){var prevState =现状后;现状后= currentReducer (cloneDeep(现状后),动作);}{返回调度:调度};}
我们的手的一个副本currentReducer当前状态和行动,它使用行动,找出与状态。返回一个修改版本的复制状态,然后用它来更新状态。同时,我们使用一个通用的cloneDeep
实现(在本例中,我们使用lodash)处理完全复制状态。简单的使用Object.assign
不适合,因为它保留了对象的引用对象属性包含的基础水平。
现在我们有了这个更新的状态,我们需要提醒应用,在乎的任何部分。这就是用户进来。我们简单地调用每个订阅功能和把他们的当前状态和以前的状态,如果谁的订阅想做三角洲比较:
函数createStore () {var现状后= {};var currentReducer =函数(状态、动作){返回状态;}var用户= [];函数调度(行动){var prevState =现状后;现状后= currentReducer (cloneDeep(现状后),动作);subscribers.forEach(函数(订户){订户(现状后,prevState);});}{返回调度:调度};}
当然,这一切真的任何好的只有默认等待减速器。我们需要的是能够添加还原剂。
添加还原剂
为了发展一个合适的reducer-adding API,让我们重新审视什么是减速机,以及我们如何可能期望使用还原剂。
在三个原则 回来的部分的文档,我们可以找到这个哲学:
“指定州树是如何改变了行动,你写纯还原剂”。
所以我们要适应的是看起来像一个州树,但状态的属性分配函数完全改变他们的状态。
{stateProperty1:函数(状态、动作){/ /做一些与国家然后返回},stateProperty2:函数(状态、动作){/ /相同},…}
是的,看起来不错。我们想把这个州树对象并运行它的每个减速器函数每次派出一个动作。
我们有currentReducer
中定义的范围,让我们创建一个新的函数,并将其分配给变量。这个函数将我们传递给它的纯粹的还原剂州树对象,并运行,每一个函数的返回结果被分配的关键。
函数createStore () {var currentReducer = function(状态、动作){返回状态;}……函数addReducers(还原剂){currentReducer = function(状态、动作){var cumulativeState = {};在还原剂(关键){cumulativeState[主要]=还原剂(例子)(国家(例子),行动);}返回cumulativeState;}}}
这里要注意的东西:我们只给一个分段国家每个减速器,键控相关的属性名。这有助于简化减速器的API,也让我们从意外改变全球其他国家地区的状态。你还原剂只应该关心自己的特定状态,但这并不妨碍你的还原剂从利用其他属性。
作为一个例子,考虑一个数据列表,假设有一个名称“待办事项”。现在考虑方法可能那种数据:通过完成任务,创建日期,等。你可以你的那种方式数据存储到单独的还原剂(例如byCompleted和byCreated)包含有序列表todoItems IDs的数据,并将他们当你去告诉他们在UI中。使用这个模型,你甚至可以重用 byCreated属性为其他类型的数据除了待办事项!这绝对是一个回家的文档的模式建议 。
现在,这是好如果我们添加一个设置还原剂的商店,但在应用程序的任何实质性的大小,根本不会是这样。所以我们应该能够适应不同的部分应用程序添加自己的还原剂。我们也应该尽量性能;也就是说,我们不应该运行相同的还原剂两次。
/ /状态树1{可见:函数(状态、动作){/ /管理可见性状态}…}/ /州树2{可见:函数(状态、动作){/ /管理可见性状态(应该是相同的功能如上所述)}…}
在上面的例子中,你可以想象两个单独的UI组件,比如能见度减速器管理是否可以看到的东西。为什么运行相同的减速器两次?答案是“愚蠢”。我们应该确保我们崩溃,键名由于性能的原因,因为所有的还原剂每次运行一个行动派。
所以记住这两个重要的因素——能力特别添加还原剂和不添加重复的还原剂,我们到达的结论是,我们应该添加另一个限定了作用域的变量,所有还原剂添加日期。
…函数createStore () {…var currentReducerSet = {};函数addReducers(还原剂){currentReducerSet =对象。分配(currentReducerSet还原剂);currentReducer =函数(状态、动作){var cumulativeState = {};(关键currentReducerSet) {cumulativeState[主要]= currentReducerSet(例子)(国家(例子),行动);}返回cumulativeState;}}…}……
varcurrentReducerSet
结合任何还原剂传递,重复的键是崩溃了。我们不必担心“失去”减速器,因为两个还原剂都将是相同的,如果他们有相同的键名。这是为什么呢?
再次重申,州树是一组key-associated纯减速机功能 。状态树的性质和减速器具有1:1的关系。不应该有两种不同的减速机的功能与同一个键相关联。
这对你应该希望照亮什么还原剂的预计:是一种行为的定义一个特定的属性。如果我们有一个“加载”属性,与我的减速机,我们说“这加载属性应该回应这个设置具体行动在这些特定的方面”。我们可以直接指定加载——认为START_LOADING动作名称”” ,或者我们可以使用它来增量加载的东西的数量,它应对行动的名字的行为,我们知道是异步的,比如实例” LOAD_REMOTE_ITEMS_BEGIN”和“LOAD_REMOTE_ ITEMS_END”。
让我们实现这个API的更多要求。我们需要能够添加和删除用户。容易:
用户函数createStore () {var = [];…函数订阅(fn) {subscribers.push (fn);}函数退订(fn) {subscribers.splice (subscribers.indexOf (fn), 1);}{返回……订阅,订阅退订:退订};}
我们需要能够提供国家当有人问。我们应该提供在一个安全的方式,所以我们将只提供它的一个副本。如上所述,我们使用cloneDeep
函数来处理这样的人不能意外变异的原始状态,因为在Javascript中,我们知道,如果有人变化在对象的引用的值,它将改变存储状态。
函数createStore () {…函数getState(){返回cloneDeep(现状后);}{返回……getState: getState};}
就是这样创造回来的!在这一点上,你应该有你需要的一切应用程序处理行为和突变状态稳定,背后的核心基本思想的回归。
让我们看一看整个事情(lodash库):
var _ =要求(“lodash”);var globalStore;getInstance()函数{如果(! globalStore) globalStore = createStore ();返回globalStore;}函数createStore () {var现状后= {};var用户= [];var currentReducerSet = {};currentReducer =函数(状态,行动){返回状态;};函数调度(行动){var prevState =现状后; currentState = currentReducer(_.cloneDeep(currentState), action); subscribers.forEach(function(subscriber){ subscriber(currentState, prevState); }); } function addReducers(reducers) { currentReducerSet = _.assign(currentReducerSet, reducers); currentReducer = function(state, action) { var ret = {}; _.each(currentReducerSet, function(reducer, key) { ret[key] = reducer(state[key], action); }); return ret; }; } function subscribe(fn) { subscribers.push(fn); } function unsubscribe(fn) { subscribers.splice(subscribers.indexOf(fn), 1); } function getState() { return _.cloneDeep(currentState); } return { addReducers, dispatch, subscribe, unsubscribe, getState }; } module.exports = getInstance();
所以我们学习通过重写回来的吗?
我们学到了一些宝贵的东西在这个经验:
我们必须保护和稳定的状态。唯一一个用户应该能够通过行为变异状态。
还原剂是纯粹的功能状态树。应用程序的状态属性都表示为一个函数,提供了更新他们的状态。每个减速器是独一无二的每个国家财产,反之亦然。
商店是单数,包含整个应用程序的状态。当我们用这种方式,我们可以跟踪每个改变应用程序的状态。
还原剂可以看作是行为的州树定义属性。
奖金部分:反应适配器
拥有商店是不错,但你可能想要使用一个框架。反应是一个显而易见的选择,因为回来的创建实现通量,反应的数据体系结构核心原则。让我们这样做!
你知道什么是酷?使其成为高阶组件,或临时有时会看到他们。我们通过的一个组件,它会创建一个新组件。也是能够无限嵌套,也就是说,特别应该能够相互嵌套,还是适当的函数。让我们从基础:
注意:要切换到ES6现在,因为它为我们提供了类的扩展,我们需要能够扩展React.Component
。
从“反应”进口的反应;出口的默认函数StoreContainer(组件,还原剂){返回类扩展的反应。组件{}}
当我们使用StoreContainer,我们通过在组件类——要么创建React.createClass
或React.Component
作为第一个参数,然后减速机状态树像上面我们创建:
/ / StoreContainer的例子使用进口StoreContainer从“StoreContainer”;从“MyReducers”进口{myReducer1, myReducer2};MyComponent StoreContainer(添加,{myReducer1, myReducer2});
酷。现在我们有一个类被创建和接收原始组件类和对象包含property-mapped还原剂。
因此,为了让这个组件工作,我们要做一些记账任务:
得到初始存储状态
绑定组件的用户设置状态
方法
还原剂添加到商店
我们可以引导这些任务在构造函数中组件的生命周期方法。让我们从初始状态开始。”
…出口的默认函数StoreContainer(组件,还原剂){返回类扩展的反应。组件构造函数(){{超级(道具);/ /我们必须调用这个创建初始反应/ /组件和得到一个“这个”价值的工作。状态= store.getState ();}}}
接下来,我们想订阅组件的设置状态
方法去商店。这使得最合理的,因为在组件上设置状态会引发自顶向下改变组件将广播,当我们想要在通量模型。
然而,我们不能简单地发送this.setState
到订阅
存储的方法——他们的参数不排队。商店想发送新老状态,和设置状态
方法只接受一个函数作为第二个参数。
所以为了解决这个问题,我们把它创建一个编组功能来处理:
…从“进口商店。/存储的;函数订户(现状后,previousState) {this.setState(现状后);}出口默认函数StoreContainer(组件,还原剂){返回类扩展的反应。组件构造函数(){{…这一点。instSubscriber = subscriber.bind(这个);store.subscribe (this.instSubscriber);}componentWillUnmount () {store.unsubscribe (this.instSubscriber);}}}…
因为商店是一个单,我们可以进口,在和直接调用的API。
我们为什么要绑定用户?因为绑定它返回一个新的函数。当卸载的组件,我们希望能够退订保持干净。我们知道,只是查找函数引用存储在其内部用户数组并删除它,因此我们需要确保我们保持参考周围我们可以得到它的时候,我们需要识别并删除它。
在构造函数中最后一件事:添加还原剂。这很简单,通过我们收到的到商店。addReducers方法:
…出口的默认函数StoreContainer(组件,还原剂){返回类扩展的反应。组件{…构造函数(){…store.addReducers(还原剂);}……}}…
现在我们已经准备好提供组件的呈现。这是特殊的本质。我们把组件我们收到并使它在的,给它赋予任何属性的特殊需要提供:
…出口的默认函数StoreContainer(组件,还原剂){返回类扩展的反应。组件{…呈现(){返回(<{…这个组件。道具}{…这个。国家}/ >);}}}…
我们是“传播 ”的属性和状态的组件是包装。这有效地确保任何属性我们传递给的包装组件,无限的可嵌套的特殊的一个至关重要的特点。这可能是也可能不是明智的地方国家作为组件上的属性,但在我的测试工作,很高兴能够通过访问状态this.props
包装对象的组件,如您所料,通常与组件接收数据从一个父组件的反应。
这是整个shabang:
从“反应”进口的反应;从“进口商店。/存储的;函数订户(现状后,previousState) {this.setState(现状后);}出口默认函数StoreContainer(组件,还原剂){返回类扩展的反应。组件{构造函数(道具){超级(道具);这一点。状态= store.getState(); this.instSubscriber = subscriber.bind(this); store.subscribe(this.instSubscriber); store.addReducers(reducers); } componentWillUnmount() { store.unsubscribe(this.instSubscriber); } render() { return ( ); } } }
使用StoreContainer实施:
从“进口StoreContainer StoreContainer ';从“MyReducers”进口{myReducer};MyComponent让添加扩展反应。组件}{/ /组件东西出口默认StoreContainer (MyComponent {myReducer});
使用组件的实现,它使用StoreContainer(一模一样正常):
MyComponent MyComponent导入添加从“添加”;从“进口ReactDOM react-dom ';
ReactDOM。MyComponent渲染(<添加myProp =“foo”/ >, document.body);
但是你不需要定义的数据基础MyComponent
立即或在一个持久类定义;你也可以做到更短暂,在实现中,这也许是明智的更通用的组件:
从“进口StoreContainer StoreContainer ';从“MyReducers”进口{myReducer};从“进口GeneralizedComponent GeneralizedComponent ';从“进口ReactDOM react-dom ';让StoreContainedGeneralizedComponent = StoreContainer (GeneralizedComponent {myReducer});ReactDOM。呈现(< StoreContainedGeneralizedComponent myProp =“foo”/ >, document.body);
这有一定的好处让父组件控制子组件的属性。
结论
通过获得一个坚实的理解回来的这个博客,我们希望团队能增强他们的状态管理,编写高效、可扩展的代码。
除了利用回来的,团队可以进一步优化产品开发过程利用Jama连接开云足球app下载官网最新版开云官网手机网页版入口® 强大的功能,如现场跟踪™ 和可追溯性得分™ ,提高工程质量,加速投放市场的时间。
开云官网手机网页版入口Jama连接使团队与增加的可见性和控制人与人之间通过使产品开发同步,工具和流程的端到端开发生命周期。开云足球app下载官网最新版了解更多。