[译] SpaceAce 了解一下,一个新的前端状态管理库

  • 原文地址:Introducing SpaceAce, a new kind of front-end state library
  • 原文作者:Jon Abrams
  • 译文出自:掘金翻译计划
  • 本文永久链接:https://github.com/xitu/gold-miner/blob/master/TODO1/introducing-spaceace-a-new-kind-of-front-end-state-library.md
  • 译者:Noah Gao
  • 校对者:Hopsken

开发前端应用的大家都知道,状态管理是开发中最重要,最具挑战性的一部分。目前流行的基于组件的视图库,如 React,包括功能齐全的(最基本的)状态管理能力。它们使应用中的每个组件都能够管理自己的状态。这对于小型应用程序来说足够了,但你很快就会感到挫败。因为决定哪些组件具有状态以及如何在组件之间共享来自每个状态的数据将会成为一个挑战。最后还要弄清楚状态是如何或为何被改变。

为了解决面向组件状态的上述问题,Redux 一类的库被引入。它们将该状态集中到一个集中的“store”中,每个组件都可以读写它。为了维护顺序,他们将改变状态的逻辑集中到应用程序的中心部分,称为 reducer,使用 actions 调用它们,并使其产生新的状态副本。它非常有效,但学习曲线很高,需要大量的样板代码,并强迫你将更新状态的代码与渲染视图的代码分开。

SpaceAce 是一个新的库,它具有 Redux 的所有优点,例如集中的 store,不可变状态,单向数据流,明确定义的 actions,它 极大地简化了代码更新 store 中状态的方式。

我们已经在 Trusted Health 的主 React 应用上用 SpaceAce 来管理状态将近一年了,取得了巨大的成功。我们的工程师团队相对较小(只有三个人),它在不加大代码复杂度和牺牲可测试性的基础上,加速了我们的功能开发。

SpaceAce 是什么?

SpaceAce 提供一个状态管理的 store 叫做一个 space。一个 space 包括只读(不可变)的状态,还有一些用于更新它的工具集。但是这个 store 里面不只是 状态,而是它本身就 状态。同时,他还提供了很多方法来生成新版本的状态。怎么做到?是一些带有属性的函数!很多 JS 开发者不知道 JS 函数也是对象。只是它能执行而已,所以它也能有一些属性,就像对象一样(因为它就是个对象!)。

每个 space 都是一个有属性的不可变对象,但是只能被读取,不能直接写入。每个 space 也是 一个函数,能够创建应用改动后的状态副本。

最后,放个例子:

import Space from 'spaceace';const space = new Space({appName: "SpaceAce demoe",user: { name: 'Jon', level: 9001 }
});const newSpace = space({ appName: "SpaceAce demo" });console.log(`Old app name: ${space.appName}, new app name: ${newSpace.appName}`);

将会输出:“Old app name: SpaceAce demoe, new app name: SpaceAce demo”

上面的例子展示了如何创建一个 space 并通过调用它将一个对象合并到状态来直接“更改”它。这和 React 的 setState 很像,应用了一次浅合并。记住,原本的 space 并没有变化,只是被一个应用了改动的副本给替换了。

然而,这对应用在有新状态时需要进行重新渲染的场景来说,没用。为了让解决这个场景更简单,一个 subscribe 函数被提供出来。它能在相关 space 被“改动”时去调用回调:

import Space, { subscribe } from 'spaceace';const space = new Space({appName: "SpaceAce demoe",user: { name: 'Jon', level: 9001 }
});subscribe(space, ({ newSpace, causedBy }) => {console.log(`State updated by ${causedBy}`);ReactDOM.render(<h1>{newSpace.appName}</h1>, document.getElementById('app'));
});// 将使 React 重新渲染
space({ appName: "SpaceAce demo" });

大多数情况下,状态都是因为用户做的事情而发生变化。比如,他们单击一个复选框、从下拉列表中选择一个选项或填入一个字段。SpaceAce 通过这些简单的交互来更新状态 非常简单。如果使用字符串调用 space,它将生成并返回处理函数:

export const PizzaForm = ({ space }) => (<form><label>Name</label><inputtype="text"value={space.name || ''}onChange={space('name')} // 当用户输入时,`space.name` 会被更新/><label>Do you like pizza?</label><inputtype="checkbox"checked={space.pizzaLover || false}onChange={space('pizzaLover')} // 分配 true 或 false 给 `space.pizzaLover`/></form>
);

虽然大多数应用只有许多简单的交互,但它们有时也会包含一些复杂的 action。SpaceAce 允许你自定义 action,所有 action 都与组件在同一文件中。调用时,会为这些 action 提供一个对象,其中包含用于更新状态的便捷函数:

import { fetchPizza } from '../apiCalls';/*handleSubmit 是一个自定义 action。第一个参数由 SpaceAce 提供。其余参数是需要传入的,在这个案例中由 React 的事件对象组成。
*/
const handleSubmit = async ({ space, merge }, event) => {event.preventDefault();// merge 函数将进行浅合并,允许一次分配多个属性merge({ saving: true }); // 立即更新 space,将触发重新渲染const { data, error } = await fetchPizza({ name: space.name });if (error) return merge({ error: errorMsg, saving: false });merge({saving: false,pizza: data.pizza // 期待得到 'Pepperoni'});
};/*handleReset 是另一个自定义 action。这个函数可以用来将 space 的所有属性抹除,将它们用另一些替换掉。
*/
const handleReset = ({ replace }) => {replace({name: '',pizzaLover: false});
};export const PizzaForm = ({ space }) => (<form onSubmit={space(handleSubmit)}>{/* ... 一些 input 元素 */}<p className="error">{space.errorMsg}</p>{space.pizza && <p>You’ve been given: {space.pizza}</p>}<button disabled={space.saving} type="submit">Get Pizza</button><button disabled={space.saving} type="button" onClick={space(handleReset)}>Reset</button></form>
);

你可能会注意到,所有这些改变 space 状态的方式都会假定状态相对较浅,但如果每个应用程序只有一个 space,那怎么可能呢?不可能的!每个 space 都可以有任意数量的 sub-space,它们也只是 space,但它们有父级。每当更新其中一个 sub-space 时,改动会冒泡,一旦更改到达根 sapce,就会触发应用的重新渲染。

有关子 space 最棒的地方在于,你不用特地去制造它,它将在你··访问 space 中的对象或是数组时,自动被创建出来:

const handleRemove = ({ remove }, itemToBeRemoved) => {// `remove` 将在数组型 space 中可用,// 它将为每个元素运行回调。// 如果回调的结果是 true,元素将被删除。remove(item => item === itemToBeRemoved);
};/*一个购物车的 space 将是一个物品的数组,每个物品都是对象,它也将是一个 space。
*/
export const ShoppingCart = ({ space }) => (<div><ul>{space.map(item => (<li key={item.uuid}><CartItemspace={item}onRemove={space(handleRemove).bind(null, item)}/></li>)}</ul></div>
);
const CartItem = ({ space, onRemove }) => (<div><strong>{space.name}</strong><inputtype="number"min="0"max="10"onChange={space('count')}value={space.count}/><button onClick={onRemove}>Remove</button></div>
);

还有很多功能可以继续探索,我很快就会分享这些有趣的技巧。请继续关注我的下一篇文章!

与此同时,你可以在 Github 上的代码和文档 中了解更多信息,也可以 让我知道你的想法!

感谢 Zivi Weinstock 的付出。

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。

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

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

相关文章

Spring MVC:带有CNVR卷的REST应用程序。 3

这是带有CNVR的Spring MVC REST教程的最后一部分。 在这里&#xff0c;我将演示所有这些东西如何工作&#xff0c;这是我在前两部分中开发的。 对于每种类型的CRUD操作&#xff0c;这将分为四个部分&#xff1a;CREATE&#xff0c;READ&#xff0c;UPDATE&#xff0c;DELETE。 …

Python学习笔记——txt文件转csv文件

import numpy as np import pandas as pdtxt np.loadtxt(data1.txt) txtDF pd.DataFrame(txt) txtDF.to_csv(file1.csv, indexFalse)转载于:https://www.cnblogs.com/yucen/p/9343574.html

左侧固定,右侧自适应的布局方式(新增评论区大佬教的方法)

一.浮动布局 1.先让固定宽度的div浮动&#xff01;使其脱离文档流。 2.margin-left的值等于固定div的宽度相等。 .aside{float: left;width: 200px;background-color: red;}.content{margin-left: 200px;background-color: blue;}<div class"aside">Lorem ipsu…

java 中io的删除文件_总结删除文件或文件夹的7种方法-JAVA IO基础总结第4篇

本文是Java IO总结系列篇的第4篇&#xff0c;前篇的访问地址如下&#xff1a;如果您阅读完成&#xff0c;觉得此文对您有帮助&#xff0c;请给我点个赞&#xff0c;您的支持是我不竭的创作动力。为了方便大家理解&#xff0c;我特意制作了本文对应的视频&#xff1a;总结删除文…

Koa2和Redux中间件源码研究

一、Koa2中间件源码分析 在Koa2中&#xff0c;中间件被存放在一个数组中。 使用koa中&#xff0c;最常见的就是app.use(fn)&#xff0c;use函数部分源码如下所示。首先中间件必须是个函数。若是generator函数&#xff0c;则需要进行转化。最后把该中间件推入middelaware数组中…

Web应用程序的简单插件系统

我们需要制作多个具有很多共享功能的基于Web的项目。 为此&#xff0c;某种插件系统将是一个不错的选择&#xff08;作为复制粘贴内容的替代方法&#xff09;。 有些框架&#xff08;例如grails&#xff09;可以选择制作Web插件&#xff0c;但大多数没有&#xff0c;因此需要实…

[转]C++ auto 关键字的使用

原文地址: https://www.cnblogs.com/KunLunSu/p/7861330.html C98 auto 早在C98标准中就存在了auto关键字&#xff0c;那时的auto用于声明变量为自动变量&#xff0c;自动变量意为拥有自动的生命期&#xff0c;这是多余的&#xff0c;因为就算不使用auto声明&#xff0c;变量依…

python模块之configparser

一 什么是configparser&#xff1f; configparser是用于解析配置文件的模块。什么是配置文件呢&#xff1f;包含配置程序信息的文件就称为配置文件。什么样的数据应该作为配置信息呢&#xff1f;需要修改但是不经常改的信息就可以作为配置信息&#xff0c;比如数据文件的路径。…

java的使用条件_Java使用条件语句和循环结构确定控制流

与任何程序设计语言一样&#xff0c;Java使用条件语句和循环结构确定控制流。本文将简单讲解条件、循环和switch。一、块作用域块(block)&#xff0c;即复合语句。是指由一对大括号括起来的若干条简单的Java语句。块确定了变量的作用域。比如&#xff1a;public class Code {st…

实现小程序canvas拖拽功能

组件地址 https://github.com/jasondu/wx-comp-canvas-drag 实现效果 如何实现 使用canvas使用movable-view标签 由于movable-view无法实现旋转&#xff0c;所以选择使用canvas 需要解决的问题 如何将多个元素渲染到canvas上如何知道手指在元素上、如果多个元素重叠如何知…

探索Apache Camel Core – Seda组件

Apache Camel中的seda组件与我在之前的博客中介绍的direct组件非常相似&#xff0c;但是以异步的方式。 为此&#xff0c;它使用java.util.concurrent.BlockingQueue作为默认实现来使消息排队并与主Route线程断开连接&#xff0c;然后在单独的线程中处理消息。 由于此BlockingQ…

Properties类和如何操作属性

Properties类继承关系java.lang.Object java.util.Dictionary<K,V> java.util.Hashtable<Object,Object> java.util.Properties所有已实现的接口&#xff1a; Serializable, Cloneable, Map<Object,Object> 直接已知子类&#xff1a; Provide…

Spring MVC:带有CNVR卷的REST应用程序。 2

在上一篇文章中&#xff0c;我快速概述了带有CNVR的Spring MVC REST项目的设置环境。 在这一部分中&#xff0c;我可以直接关注控制器和REST服务的演示。 通常&#xff0c;我将做一个简短的介绍&#xff0c;然后我将介绍控制器方法并解释所有关键时刻。 由于我将进一步讨论RES…

SCP 报错 not a regular file

在 scp 后 加 -r转载于:https://www.cnblogs.com/LYliangying/p/9815534.html

H5页面滚动阻尼效果实现

功能描述 要求 页面分为AB两个区域 当手机可视区的底部接触到 “阻尼带” 的时候&#xff0c;有个上拉弹性过程 当上拉到一定阈值程度就直接把B区顶部弹到手机可视区的顶部&#xff0c;让可视区从B区开始显示当上拉程度未到阈值&#xff0c;就回弹复原 当手机可视区从B区向上…

java面试题(杨晓峰)---第五讲String、StringBuffer、StringBuilder有什么区别?

线程 字符 操作频繁度 1 String &#xff08;1&#xff09;String的创建机制 由于String在java世界中使用过于频繁&#xff0c;java为了避免在一个系统中产生大量重复的String对象&#xff0c;引入了字符串常量池&#xff0c;其运行机制是&#xff1a;创建一个字符串时&am…

mysql怎么按年份分组_mysql - MYSQL按ID分组,但根据最近的年份进行拉取 - SO中文参考 - www.soinside.com...

我有一个包含以下内容的表&#xff1a;StudID Name Year SubjectID SubjectName MTFlag51280 ALOYSIUS 2019 42 CHINESE LANGUAGE 151280 ALOYSIUS 2020 70 ENGLISH LANGUAGE 051280 ALOYSIUS 2020 95 CHINESE B 151280 ALOYSIUS 2020 75 MATHEMATICS 051290 AMIL 2020 70 ENGL…

面向 Web 前端的原生语言总结手册

这一系列文章旨在让具有 Web 前端背景的开发者快速上手原生语言。 背景与动机 从 WebView 到 Hybrid 再到 React Native&#xff0c;移动端主流技术方案中前端同学的施展空间越来越大。但传统 Web 前端背景的同学所熟悉的编程语言主要是 JavaScript&#xff0c;在与 Native 协…

Java 8的新增功能(第二部分–可能会出现什么)

免责声明&#xff1a;我不为Oracle工作&#xff0c;也不以任何方式代表Oracle。 此功能列表不是官方的。 作为“局外人”&#xff0c;这只是我研究的一部分。 这是由三部分组成的系列文章的第二部分。 在第一部分中 &#xff0c;我谈到了Oracle正式让开发人员知道JavaFX 8中应…

MariaDB卸载

二进制安装方式的MariaDB卸载 关闭mysql服务service mysql stop 或 /etc/init.d/mysql stop 或 mysqladmin shutdown -uroot -p 删除数据文件和目录whereis mysql find / -name mysql rm -rf xxx 删除软链接&#xff0c;二进文件&#xff08;如有必要&#xff09;cd /usr/local…