[MobX State Tree数据组件化开发][3]:选择正确的types.xxx

?系列文章目录?

定义Model时,需要正确地定义props中各字段的类型。本文将对MST提供的各种类型以及类型的工厂方法进行简单的介绍,方便同学们在定义props时挑选正确的类型。

前提

定义props之前,有一个前提是,你已经明确地知道这个Model中状态的数据类型。

如果Model用于存放由后端API返回的数据,那么一定要和后端确认返回值在所有情况下的类型。比如,某个字段在没有值的时候你以为会给一个'',而后端却给了个null;或者某个数组你以为会给你一个空数组[],他又甩你一个null如果你不打算自己编写一个标准化数据的方法,那一定要和数据的提供方确定这些细节。

基础类型

types.string

定义一个字符串类型字段。

types.number

定义一个数值类型字段。

types.boolean

定义一个布尔类型字段。

types.integer

定义一个整数类型字段。

注意,即使是TypeScript中也没有“整数”这个类型,在编码时,传入一个带小数的值TypeScript也无法发现其中的类型错误。如无必要,请使用types.number

types.Date

定义一个日期类型字段。

这个类型存储的值是标准的Date对象。在设置值时,可以选择传入数值类型的时间戳或者Date对象。

export const Model = types.model({date: types.Date }).actions(self => ({setDate (val: Date | number) {self.date = date;}}));
复制代码

types.null

定义一个值为null的类型字段。

types.undefined

定义一个值为undefined的类型字段。

复合类型

types.model

定义一个对象类型的字段。

types.array

定义一个数组类型的字段。

types.array(types.string);
复制代码

上面的代码定义了一个字符串数组的类型。

types.map

定义一个map类型的字段。该map的key都为字符串类型,map的值都为指定类型。

types.map(types.number);
复制代码

可选类型,types.optional

根据传入的参数,定义一个带有默认值的可选类型。

types.optional是一个方法,方法有两个参数,第一个参数是数据的真实类型,第二个参数是数据的默认值。

types.optional(types.number, 1);
复制代码

上面的代码定义了一个默认值为1的数值类型。

注意,types.array或者types.map定义的类型自带默认值(array为[],map为{}),也就是说,下面两种定义的结果是一样的:

// 使用types.optional
types.optional(types.array(types.number), []);
types.optional(types.map(types.number), {});// 不使用types.optional
types.array(types.number);
types.map(types.number);
复制代码

如果要设置的默认值与types.arraytypes.map自带的默认值相同,那么就不需要使用types.optional

自定义类型,types.custom

如果想控制类型更底层的如序列化和反序列化、类型校验等细节,或者根据一个class或interface来定义类型,可以使用types.custom定义自定义类型。

class Decimal {...
}const DecimalPrimitive = types.custom<string, Decimal>({name: "Decimal",fromSnapshot(value: string) {return new Decimal(value)},toSnapshot(value: Decimal) {return value.toString()},isTargetType(value: string | Decimal): boolean {return value instanceof Decimal},getValidationMessage(value: string): string {if (/^-?\d+\.\d+$/.test(value)) return "" // OKreturn `'${value}' doesn't look like a valid decimal number`}
});
复制代码

上面的代码定义了一个Decimal类型。

联合类型,types.union

实际开发中也许会遇到这样的情况:一个值的类型可能是字符串,也可能是数值。那我们就可以使用types.union定义联合类型:

types.union(types.number, types.string);
复制代码

联合类型可以有任意个联合的类型。

字面值类型,types.literal

字面值类型可以限制存储的内容与给定的值严格相等。

比如使用types.literal('male')定义的状态值只能为'male'

实际上,上面提到过的types.null以及types.undefined就是字面值类型:

const NullType = types.literal(null);
const UndefinedType = types.literal(undefined);
复制代码

搭配联合类型,可以这样定义一个性别类型:

const GenderType = types.union(types.literal('male'), types.literal('female'));
复制代码

枚举类型,types.enumeration

枚举类型可以看作是联合类型以及字面值类型的一层封装,比如上面的性别可以使用枚举类型来定义:

const GenderType = types.enumeration('Gender', ['male', 'female']);
复制代码

方法的第一个参数是可选的,表示枚举类型的名称。第二个参数传入的是字面值数组。

在TypeScript环境下,可以这样搭配TypeScript枚举使用:

enum Gender {male,female
}const GenderType = types.enumeration<Gender>('Gender', Object.values(Gender));
复制代码

可undefined类型,types.maybe

定义一个可能为undefined的字段,并自带默认值undefined

types.maybe(type)
// 等同于
types.optional(types.union(type, types.literal(undefined)), undefined)
复制代码

可空类型,types.maybeNull

types.maybe类似,将undefined替换成了null

types.maybeNull(type)
// 等同于
types.optional(types.union(type, types.literal(null)), null)
复制代码

不可不类型,types.frozen

frozen意为“冻结的”,types.frozen方法用来定义一个immutable类型,并且存放的值必须是可序列化的。

当数据的类型不确定时,在TypeScript中通常将值的类型设置为any,而在MST中,就需要使用types.frozen定义。

const Model = types.model('Model', {anyData: types.frozen()}).actions(self => ({setAnyData (data: any) {self.anyData = data;}}));
复制代码

在MST看来,使用types.frozen定义类型的状态值是不可变的,所以会出现这样的情况:

model.anyData = {a: 1, b: 2}; // ok, reactive
model.anyData.b = 3; // not reactive
复制代码

也就是只有设置一个新的值给这个字段,相关的observer才会响应状态的更新。而修改这个字段内部的某个值,是不会被捕捉到的。

滞后类型,types.late

有时候会出现这样的需求,需要一个Model A,在A中,存在类型为A本身的字段。

如果这样写:

const A = types.model('A', {a: types.maybe(A), // 使用mabe避免无限循环});
复制代码

会提示Block-scoped variable 'A' used before its declaration,也就是在A定义完成之前就试图使用他,这样是不被允许的。

这个时候就需要使用types.late

const A = types.model('A', {a: types.maybe(types.late(() => A))});
复制代码

types.late需要传入一个方法,在方法中返回A,这样就可以避开上面报错的问题。

提纯类型,types.refinement

types.refinement可以在其他类型的基础上,添加额外的类型校验规则。

比如需要定义一个email字段,类型为字符串但必须满足email的标准格式,就可以这样做:

const EmailType = types.refinement('Email',types.string,(snapshot) => /^[a-zA-Z_1-9]+@\.[a-z]+/.test(snapshot), // 校验是否符合email格式
);
复制代码

引用与标识类型

拿上一篇文章中的TodoList作为例子,我们在对Todo列表中的某一个Todo进行编辑的时候,需要通过id跟踪这个Todo,在提交编辑结果时,通过这个id找到对应的Todo对象,然后进行更新。

这种需要跟踪、查找的需求很常见,写多了也觉得麻烦。

好在MST提供了一个优雅的解决方案:引用类型和标识类型。

这两者需要搭配使用才能发挥作用:

定义标识,types.identifier

标识就是数据对象的唯一标识字段,这个字段的值在库中保持唯一,也就是primary_key。

比如上一篇文章中的TodoItem,可以改造为:

export const TodoItem = types.model('TodoItem', {id: types.identifier,title: types.string,done: types.boolean,});
复制代码

使用引用类型进行跟踪,types.reference

改造TodoList:

export const TodoList = types.model('TodoList', {...list: types.array(TodoItem),editTarget: types.reference(TodoItem),...});
复制代码

然后在创建Model实例,或者applySnapshot的时候,可以将editTarget的值设定为正在编辑的TodoItem的id值,MST就会自动在list中查找id相同的TodoItem:

const todoList = TodoList.create({list: [{id: '1', title: 'Todo 1', done: true},{id: '2', title: 'Todo 2', done: true},...],editTarget: '1'
});//此时的editTarget就是list中id为'1'的TodoItem对象
console.log(todoList.list[0] === todoList.editTarget); // truetodoList.editTarget = todoItem2; // todoItem2为id为'2'的TodoItem对象
console.log(getSnapshot(todoList).editTarget === '2'); // truetodoList.editTarget = '2' as any;
console.log(getSnapshot(todoList).editTarget === '2'); // true
复制代码

上面的代码说明,reference类型的字段本质上维护的是目标的标识字段值,并且,除了将目标对象赋值给reference字段外,将目标标识字段值赋值给reference字段的效果是一样的。

另外,reference不仅仅能搭配array使用,也能在map中查找:

const TodoList = types.model('TodoList', {todoMap: types.map(TodoItem),editTarget: types.reference(TodoItem)
});
复制代码

甚至,MST也允许你自定义查找器(resolver),给types.reference指定第二个参数,比如官网的这个例子:

const User = types.model({id: types.identifier,name: types.string
})const UserByNameReference = types.maybeNull(types.reference(User, {// given an identifier, find the userget(identifier /* string */, parent: any /*Store*/) {return parent.users.find(u => u.name === identifier) || null},// given a user, produce the identifier that should be storedset(value /* User */) {return value.name}})
)const Store = types.model({users: types.array(User),selection: UserByNameReference
})const s = Store.create({users: [{ id: "1", name: "Michel" }, { id: "2", name: "Mattia" }],selection: "Mattia"
})
复制代码

types.identifierNumber

若对象的唯一标识字段的值为数值类型,那么可以使用types.identifierNumber代替types.identifier

types.safeReference

这是一个“安全”的引用类型:

const Todo = types.model({ id: types.identifier })
const Store = types.model({todos: types.array(Todo),selectedTodo: types.safeReference(Todo)
});
复制代码

selectedTodo引用的目标从todos这个节点被移除后,selectedTodo会自动被设置为undefined

小结

MST提供的类型和类型方法非常齐全,利用好他们就能为任意数据定义恰当的类型。

喜欢本文的欢迎关注+收藏,转载请注明出处,谢谢支持。

转载于:https://juejin.im/post/5c526c6451882542ff1297ed

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

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

相关文章

ubuntu系统备份和还原_如何使用Aptik在Ubuntu中备份和还原您的应用程序和PPA

ubuntu系统备份和还原If you need to reinstall Ubuntu or if you just want to install a new version from scratch, wouldn’t it be useful to have an easy way to reinstall all your apps and settings? You can easily accomplish this using a free tool called Apti…

rest_framework09:自动生成接口文档(简略)

coreapi 参考 python/Django-rest-framework框架/8-drf-自动生成接口文档 | Justin-刘清政的博客 Swagger 很多语言都支持&#xff0c;看起来用的人多。 参考fastapi的界面

AppDomainManager后门的实现思路

本文讲的是AppDomainManager后门的实现思路&#xff0c;0x00 前言从Casey SmithsubTee学到的一个技巧&#xff1a;针对.Net程序&#xff0c;通过修改AppDomainManager能够劫持.Net程序的启动过程。 如果劫持了系统常见.Net程序如powershell.exe的启动过程&#xff0c;向其添加…

所有内耗,都有解药。

你是否常常会有这种感觉&#xff1a;刚开始接手一件事情&#xff0c;脑海中已经幻想出无数个会发生的问题&#xff0c;心里也已笃定自己做不好&#xff1b;即使别人不经意的一句话&#xff0c;也会浮想一番&#xff0c;最终陷入自我怀疑&#xff1b;随便看到点什么&#xff0c;…

ABAP 通过sumbit调用另外一个程序使用job形式执行-简单例子

涉及到两个程序&#xff1a; ZTEST_ZUMA02 (主程序)ZTEST_ZUMA(被调用的程序&#xff0c;需要以后台job执行)"ztest_zuma 的代码DATA col TYPE i VALUE 0.DO 8 TIMES.MESSAGE JOB HERE TYPE S.ENDDO.程序ZTEST_ZUMA是在程序ZTEST_ZUMA02中以job的形式调用的&#xff0c;先…

那些影响深远的弯路

静儿最近反思很多事情&#xff0c;不仅是当时做错了。错误定式形成的思维习惯对自己的影响比事情本身要大的多。经常看到周围的同事&#xff0c;非常的羡慕。他们都很聪明、有自己的方法。就算有些同事工作经验相对少一些&#xff0c;但是就像在废墟上创建一个辉煌的城市要比在…

如何使用APTonCD备份和还原已安装的Ubuntu软件包

APTonCD is an easy way to back up your installed packages to a disc or ISO image. You can quickly restore the packages on another Ubuntu system without downloading anything. APTonCD是将安装的软件包备份到光盘或ISO映像的简便方法。 您可以在不下载任何东西的情况…

rest_framework10:base64补充/修改头像

base64补充 # base64 变长&#xff0c;可反解 # md5 固定长度&#xff0c;不可反解# base64 编码和解码 import base64 import json dic{name:test,age:18} dic_strjson.dumps(dic)retbase64.b64encode(dic_str.encode(utf-8)) print(ret)# 解码 ret2base64.b64decode(ret) pri…

next_permutation(全排列算法)

next_permutation(全排列算法) STL提供了两个用来计算排列组合关系的算法&#xff0c;分别是next_permutation和prev_permutation。 首先解释下全排列&#xff0c;顾名思义&#xff0c;即一组数的全部排列的情况。 next_permutation 即列出一组数的全部排列情况&#xff0c;不过…

C#自定义字符串压缩和解压缩源码库

如下的内容是关于C#自定义字符串压缩和解压缩库的内容。class ZipLib{public static string Zip(string value){byte[] byteArray new byte[value.Length];int indexBA 0;foreach (char item in value.ToCharArray()){byteArray[indexBA] (byte)item;}System.IO.MemoryStrea…

使用 Visual Studio 2022 调试Dapr 应用程序

使用Dapr 编写的是一个多进程的程序, 两个进程之间依赖于启动顺序来组成父子进程&#xff0c;使用Visual Studio 调试起来可能会比较困难&#xff0c;因为 Visual Studio 默认只会把你当前设置的启动项目的启动调试。好在有Visual Studio 扩展&#xff08;Microsoft Child Proc…

卸载 cube ui_如何还原Windows 8附带的已卸载现代UI应用程序

卸载 cube uiWindows 8 ships with built-in apps available on the Modern UI screen (formerly the Metro or Start screen), such as Mail, Calendar, Photos, Music, Maps, and Weather. Installing additional Modern UI apps is easy using the Windows Store, and unins…

rest_framework11:jwt简单例子/自定制基于jwt认证类

jwt简单例子 一、登陆设置 1.不需要写login的视图类&#xff0c;使用jwt内置的。 2.需要前置条件&#xff0c;已有继承AbstractUser models,并且有数据&#xff0c;用于校验&#xff0c;返回token。 urls.py from rest_framework_jwt.views import obtain_jwt_tokenurlpat…

Java各种数据类型,自己学习写的笔记!!!

java编程规范&#xff1a; 1.良好的标识符的命名保留字不能作为标识符命名&#xff1a; class、public、static..., goto,const区分大小写&#xff1a;helloWorld、HelloWorld 2.良好的注释习惯 3.良好的缩进&#xff1a;没遇到一个代码块缩进一次&#xff08;一个tab键&…

Java Decompiler(Java反编译工具)

Java Decompiler官网地址&#xff1a;http://jd.benow.ca/ 官网介绍&#xff1a; The “Java Decompiler project” aims to develop tools in order to decompile and analyze Java 5 “byte code” and the later versions. JD-Core is a library that reconstructs Java sou…

20位程序员关于求职的疑问,以及我给出的参考答案

作者&#xff1a;陆小凤首发&#xff1a;公众号【程序员江湖】阅读本文大概需要 6 分钟。前几天发了一条朋友圈对于求职小伙伴们提出的问题&#xff0c;我进行了收集整理&#xff0c;统一反馈。也许这20个问题也是你们遇到的问题&#xff0c;所以趁着年前赶紧把它发出来。以下2…

MassTransit | 基于MassTransit Courier 实现 Saga 编排式分布式事务

Saga 模式Saga 最初出现在1987年Hector Garcaa-Molrna & Kenneth Salem发表的一篇名为《Sagas》的论文里。其核心思想是将长事务拆分为多个短事务&#xff0c;借助Saga事务协调器的协调&#xff0c;来保证要么所有操作都成功完成&#xff0c;要么运行相应的补偿事务以撤消先…

ccleaner无法更新_CCleaner正在静默更新关闭自动更新的用户

ccleaner无法更新CCleaner is forcing updates on users who specifically opt out of automatic updates. Users will only find out about these unwanted updates when they check the version number. CCleaner强制对专门选择退出自动更新的用户进行更新。 用户只有在检查版…

查找域内所有的Windows Server 2012 R2的服务器,并区分出哪些是物理机,那些是虚拟机...

通过使用Get-Adcomputer和Get-Wmiobject 组合来实现。思路是这样的&#xff0c;先看一台服务器的属性值有什么可用利用的。[12r2-dc]: PS C:\> Get-ADComputer -Identity 12r2-dc -Properties *AccountExpirationDate :accountExpires …

rest_framework12:多登陆方式与自动签发token/配置过期时间

多登陆方式与自动签发token views.py 1.继承Viewset&#xff0c;方法里可以使用自定义login&#xff0c;更直观。需要路由直接配置请方式 2. 序列化是直接对request数据处理&#xff0c;并从对象中获取token 3.context可以储存自定义数据 # 多登陆方式&#xff0c;自动签发…