[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.pswp.cn/news/280419.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280419.shtml
英文地址,請注明出處:http://en.pswp.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;自動簽發…