В моята последна статия обясних мотивацията на моя екип да използва Microstates.js в нашето приложение Ember и какви са били печалбите. В тази статия ще се потопя по-дълбоко в това как работят Microstates. Също така ще дам някои полезни съвети за работа със сложни микросъстояния.

Какво дори е микродържава?

За да разбера някои от нюансите на работа със сложни типове, първо трябва да обясня какво представляват Microstates и как работят на високо ниво.

Моята интерпретация на думата „микродържава“ се разбира най-лесно, когато я съпоставя с думата „макродържава“. Склонен съм да мисля за „макросъстояние“ като вид състояние, което бихте запазили в базата данни. Това е нещото, с което Ember Data се справя много добре и често е центрирано около домейна на вашето приложение.

Сравнете това с „микросъстояния“, които аз тълкувам като видове състояния, които не се запазват в базата данни. Вместо това този вид състояние най-често се вижда в чистата UX логика: „модалът отворен ли е или затворен“, „в коя колона и посока е сортирана таблицата“, „зелен или син бутон е“ и т.н. В Ember ние обикновено съхранява този вид състояние в компонент или контролер. Въпреки това, както показах в последната си статия, това може да стане тромаво, когато се работи с големи блокове от състояние или дълбоко вложени йерархии на компоненти.

По-техническа дефиниция

По отношение на библиотеката Microstates.js, „микросъстоянието“ е защитна кутия около вашите данни за състоянието. Когато създавате нов обект Microstate, често му давате първоначална стойност.

Microstate защитава вашите данни за състоянието от директна мутация. Той прави това, като позволява само „записи“ чрез методите за преход на Microstate. Методите за преход гарантират, че се създава чисто нов обект, вместо новият да бъде мутиран.

Тъй като обектите Microstate защитават вашето състояние по този начин, това влияе върху начина, по който осъществявате достъп до състоянието, когато трябва да го „четете“. За прости типове (Boolean, String, Number) трябва да използвате .state за достъп до данните.

За сложни типове (Object, Array, потребителски типове), трябва да използвате функцията valueOf, за да изведете състоянието.

Изискването за използване на valueOf и .state за достъп до данни в Microstate е различно от това, с което сме свикнали в Ember, но това е полезен компромис.

Сега, след като разбираме по-добре какво е Microstate, нека да разгледаме някои специални съображения за сложни типове.

Микросъстояния на масив

Първото нещо, което трябва да знаете за Array Microstates е, че те не са Масиви. Те биха били по-добре описани като „подобни на масиви обекти“. Microstate Arrays използват Symbol.iterator, за да ги направят итерируеми. Това им дава някои от същите способности като масив, но не всички.

Нещата, които те могат да направят:

  • Преминете с for или {{#each}}
  • Разпръскване [...myArrayMicrostate]
  • Използвайте версията Microstate на някои Array методи

Неща, които те не могат да правят:

  • Достъп по индекс myArrayMicrostate[2]
  • Използвайте нормални методи на масива

Как да стигна до конкретен индекс?

Вероятно най-забележимото и разочароващо нещо в списъка не мога е невъзможността за достъп до стойностите на Array Microstate при конкретен индекс. За щастие, заобиколното решение е доста просто: [...myArrayMicrostate][2].

Array Microstate може да използва оператора за разпространение, което означава, че можем да копираме съдържанието на Array Microstate в обикновен масив и след това да използваме индекса.

Какво ще кажете за моите методи за масив?

Обектът Array Microstate ни дава някои „методи“ на Array, с които сме свикнали, като push и pop, но те не се държат като естествените еквиваленти на Array. Във версията на Microstate всички методи прехвърлят вашия Microstate към напълно нов обект, докато някои от основните методи на Array променят масива на място.

Някои методи като reduce и методите Ember ArrayProxy като findBy напълно липсват като еквивалент на Microstate. В тези случаи ще трябва да следвате същия модел, който използвахме за достъп до индекса, и да копирате Microstate Array с разширение [...myArrayMicrostate].reduce(). Същото правило важи, ако искате да използвате родната версия на метода вместо тази на Microstate. Например [...myArrayMicrostate].map() ще ви върне нов естествен масив, вместо да прехвърли микросъстоянието в ново състояние.

Масив от Microstates срещу Microstate Array

И двете структури от данни могат да се използват, но трябва да внимавате да изберете правилната за правилната работа. За да избегна объркване и да запазя последователност, се опитвам да остана с Microstate Array колкото е възможно повече create([Person]). Понякога обаче ще работя с естествен масив от микросъстояния [create(Person), create(Person)], ако имам нужда от достъп до собствени методи на масив, както обясних по-горе. След като свърша работата си с естествените методи на Array, често конвертирам резултата обратно в Microstate Array create([Person], normalArray.map(myMicrostate => valueOf(myMicrostate)).

Микросъстояния на обекти

Микросъстоянията на обекти са подобни на родните JS обекти по много начини с няколко фини разлики:

  • Променете стойностите с myObjectMicrostate.put()
  • Изтриване на ключове с myObjectMicrostate.delete()
  • Всички обектни данни се намират вътре в entries. myObjectMicrostate.entries.foo
  • Всички ключове са в имота keys. myObjectMicrostate.keys
  • Всички стойности са в свойството values. myObjectMicrostate.values

Сега нека разгледаме някои съображения, които са общи за всички сложни типове Microstate.

Равенство

Когато създавате микросъстояние със сложен тип, то се съхранява в микросъстоянието по референция,не по стойност.

Това означава, че ако създадете две микросъстояния с един и същ обект, стойностите ще бъдат равни, въпреки че самите микросъстояния не са.

Преходи

Микросъстоянието ще препраща към оригиналния обект, ДОКАТО не извикате преход към него.

Когато извикате метод на преход, създаденото ново микросъстояние ще копира целия обект и няма да промени оригинала.

Стойността ще бъде копирана в нов обект, независимо колко дълбоко в дървото промените нещо.

Използване на valueOf

Не забравяйте да import

Има глобална функция valueOf, която ще се използва, ако не импортирате функцията от @microstates/ember. Ако използвате глобалния, вашият линтер вероятно няма да го улови като undefined променлива. Ще видите и неочаквани грешки, когато се опитате да го използвате като:Cannot convert undefined or null to object

стойност на Template Helper

Има {{value-of}} помощник, който можете да използвате във вашите шаблони, когато трябва да прехвърлите POJO в компонент вместо обект Microstate.

Извършване на модификации директно на стойността

Когато искате да използвате метод на масив като replace или reduce, на микросъстояние на масив, може да бъде много изкушаващо да използвате valueOf(myMicrostateArray) и да изпълните своя метод върху върнатия масив. БЪДИ ВНИМАТЕЛЕН. Ако направите това, вие губите всички гаранции за неизменност и въвеждате възможността за модифициране на данни за други променливи, сочещи към вашия обект. Това е особено опасно, когато използвате метод „мутиране на място“, като pop или push. Вместо това препоръчвам да се придържате към разпространението на Microstate Array в нов собствен масив и да направите вашата модификация по този начин. valueOf беше предназначен за четене, а не за писане.

Сложни типове и магазин

В моята последна статия обясних основите на функцията Microstate Store. Един нюанс, който трябва да знаете, когато работите с Microstate, който е обвит в Store, е, че върнатата стойност след преход може да не е пълното Microstate дърво. Микросъстояние, обвито в Store, ще върне най-дълбокото свойство, извършило прехода.

Този нюанс прави възможно да не се излага целият обект на състояние с всеки компонент, който го използва. Компонентите са наясно само с частите от състоянието, които са подходящи за тях.

Стойности по подразбиране

Когато създавате персонализиран тип Microstate, вие използвате нотация на полето на класа върху атрибутите, за да посочите какъв тип трябва да има всеки атрибут.

Едно от красивите неща за спецификацията на полето на класа е, че дава на JS класовете начин да определят стойности по подразбиране. И така, как да направим това с Microstates, когато вече използваме стойността на полето на класа, за да посочим типа на атрибута? Можете да зададете стойност по подразбиране, като създадете Microstate с типа и стойността, които искате да са по подразбиране.

По подразбиране за микросъстоянието, а не за стойността

Едно нещо, което трябва да знаете, когато работите със стойности по подразбиране, е, че те се прилагат само към обекта Microstate, а не към основната стойност.

В този пример създаваме Person Microstate с празен обект. Това означава, че когато имаме достъп до valueOf Microstate, ще получим обратно празния обект без стойностите по подразбиране. Това е така, защото Microstates са обвивка около стойността, която даваме, и не ги манипулират, докато изрично не им кажем да направят преход.

Въпреки това, както можем да видим в горния пример, дори след преход само засегнатите свойства ще бъдат част от „стойността“. Свойствата, които не са прехвърлени, все още няма да имат стойности по подразбиране за основната „стойност“.

Заключение

Микросъстоянията ни дават мощен начин да работим с предвидимо, неизменно състояние с кратък и прост синтаксис. Има някои тънкости, с които ще се сблъскате, когато работите със сложни типове, но сега, след като сте наясно с тях, вие имате още по-голяма власт да управлявате състоянието на потребителския си интерфейс.

Какво следва?

В следващата си статия ще дам някои модели, подобни на готварска книга, които можете да направите с Microstates, които ще ви помогнат да направите живота си по-лесен.