React + Mobx: apa gunanya?

Hari ini saya ingin memberi tahu Anda tentang bagaimana transisi ke Mobx terjadi pada proyek kami, apa keuntungannya. Proyek tipikal juga akan ditampilkan dan penjelasan tentang masalah utama akan diberikan. Tapi pertama-tama, perkenalan.

gambar

Mengapa Anda perlu beralih ke sesuatu? Sebenarnya, jawaban untuk pertanyaan ini sudah setengah dari pertempuran. Banyak sekarang senang menerapkan teknologi baru hanya karena mereka baru. Garis yang bagus dalam resume, kemungkinan pengembangan diri, berada dalam tren. Ini bagus ketika Anda bisa maju.

Namun, setiap alat harus menyelesaikan masalahnya sendiri, dan kami menolaknya dengan satu atau lain cara ketika kami menulis kode komersial.

Dalam proyek kami, ada sejumlah widget tempat pengguna memasukkan data mereka, berinteraksi dengan formulir. Sebagai aturan, setiap widget memiliki beberapa layar. Sekali waktu, semuanya bekerja pada MarkoJS + templating engine lama yang diperlukan jQuery pada klien. Interaksi dengan bentuk-bentuk ditulis dalam gaya imperatif, jika ... yang lain, panggilan balik dan ini semua sama yang sudah ada di masa lalu.

Kemudian tiba saatnya untuk Bereaksi . Logika bisnis pada klien semakin tebal, ada banyak opsi interaksi, kode penting berubah menjadi kekacauan yang rumit. Kode reaksi deklaratif ternyata jauh lebih nyaman. Akhirnya dimungkinkan untuk berkonsentrasi pada logika daripada presentasi, menggunakan kembali komponen dan dengan mudah mendistribusikan tugas untuk mengembangkan fitur baru antara karyawan yang berbeda.

Tetapi aplikasi pada murni Bereaksi dari waktu ke waktu bertumpu pada batas yang nyata. Tentu saja, kami bosan menulis ini. SetState untuk semua orang dan memikirkan asinkroninya, tetapi melempar data dan panggilan balik melalui ketebalan komponen membuatnya sangat sulit. Singkatnya, saatnya telah datang untuk sepenuhnya memisahkan data dan presentasi. Ini bukan pertanyaan tentang bagaimana Anda dapat mengatur untuk melakukannya pada Bereaksi murni, tetapi dalam kerangka kerja industri yang menerapkan arsitektur Flux dari aplikasi front-end telah populer akhir-akhir ini.

Menurut jumlah artikel dan referensi di lowongan, Redux adalah yang paling terkenal di antara kita. Sebenarnya, saya sudah membawa tangan saya untuk menginstalnya di proyek kami dan memulai pengembangan, seperti pada saat-saat terakhir (dan ini benar-benar!) Iblis menarik Habr, dan kemudian hanya ada diskusi tentang topik "Redux atau Mobx?" Inilah artikel ini: habr.com/en/post/459706 . Setelah membacanya, dan juga semua komentar di bawahnya, saya menyadari bahwa saya masih akan menggunakan Mobx .

Jadi lagi. Jawaban untuk pertanyaan paling penting - mengapa semua ini? - kelihatannya seperti ini: saatnya untuk memisahkan presentasi dan data, saya ingin membangun manajemen data dalam gaya deklaratif (seperti menggambar), tidak ada penyerbukan silang dari callback dan atribut yang diteruskan.

Sekarang kita siap untuk melanjutkan.

1. Tentang aplikasi


Kita perlu membangun di depan seorang perancang layar dan bentuk, yang kemudian dapat dengan cepat dikocok, terhubung satu sama lain mengikuti perubahan persyaratan bisnis. Hal ini tak terhindarkan menggerakkan kami ke yang berikut: untuk membuat koleksi komponen yang benar-benar terisolasi, serta beberapa komponen dasar yang sesuai dengan masing-masing widget kami (pada kenyataannya, ini adalah SPA terpisah yang dibuat setiap kali berdasarkan kasus bisnis baru dalam aplikasi umum).

Contoh-contoh akan menunjukkan versi terpotong dari salah satu gadget ini. Agar tidak menumpuk kode tambahan, biarkan itu berupa tiga bidang input dan tombol.

2. Data


Mobx pada dasarnya bukan kerangka kerja, melainkan hanya perpustakaan. Manual secara eksplisit menyatakan bahwa itu tidak mengatur data Anda secara langsung. Anda sendiri harus memunculkan organisasi semacam itu. Omong-omong, kami menggunakan Mobx 4 karena versi 5 menggunakan tipe data Sybmol, yang, sayangnya, tidak didukung oleh semua browser.

Jadi, semua data dialokasikan ke entitas yang terpisah. Aplikasi kami bertujuan untuk satu set dua folder:
- komponen tempat kita meletakkan semua tampilan
- toko , yang akan berisi data, serta logika bekerja dengan mereka.
Sebagai contoh, komponen entri data khas untuk kami terdiri dari dua file: Input.js dan InputStore.js . File pertama adalah komponen React bodoh yang sepenuhnya bertanggung jawab untuk menampilkan, yang kedua adalah data komponen ini, aturan pengguna ( onClick , onChange , dll ...)

Sebelum kita langsung ke contoh, kita perlu memecahkan masalah penting lainnya.

3. Manajemen


Ya, kami memiliki komponen yang sepenuhnya otonom dari View-Store, tetapi bagaimana kita berkumpul bersama dalam seluruh aplikasi? Untuk tampilan, kita akan memiliki komponen root dari App.js , dan untuk mengelola aliran data, penyimpanan utama adalah mainStore.js . Prinsipnya sederhana: mainStore tahu segalanya tentang semua repositori dari semua komponen yang diperlukan (akan ditunjukkan di bawah bagaimana ini dicapai). Repositori lain tidak tahu apa-apa tentang dunia sama sekali (well, akan ada satu pengecualian - kamus). Dengan demikian, kami dijamin tahu ke mana data kami pergi dan ke mana harus mencegatnya.

gambar

mainStore secara deklaratif, melalui perubahan bagian-bagian dari keadaannya, dapat mengontrol sisa komponen. Dalam gambar berikut, Tindakan dan Status merujuk ke penyimpanan komponen, dan nilai yang dihitung merujuk ke mainStore :

gambar

Mari kita mulai menulis kode. File aplikasi utama index.js:

import React from "react"; import ReactDOM from "react-dom"; import {Provider} from "mobx-react"; import App from "./components/App"; import mainStore from "./stores/mainStore"; import optionsStore from "./stores/optionsStore"; //  IE11 require("es6-object-assign").polyfill(); require( "./static/less/main.less"); const stores = { mainStore, optionsStore, ButtonStore : mainStore.ButtonStore, FioStore : mainStore.FioStore, EmailStore : mainStore.EmailStore }; ReactDOM.render(( <Provider {...stores}> <App /> </Provider> ), document.getElementById('reactContainer')); 

Di sini Anda dapat melihat konsep dasar Mobx. Data (toko) tersedia di mana saja dalam aplikasi melalui mekanisme Penyedia . Kami membungkus aplikasi kami dengan mendaftar fasilitas penyimpanan yang dibutuhkan. Untuk menggunakan Penyedia, kami menghubungkan modul mobx-react . Agar toko kontrol utama mainStore memiliki akses ke semua data lain sejak awal, kami menginisialisasi toko anak dalam mainStore :

 // mainStore.js import optionsStore from "./optionsStore"; import ButtonStore from "./ButtonStore"; import FioStore from "./FioStore"; import EmailStore from "./EmailStore"; .... class mainStore { constructor() { /** *    */ this.ButtonStore = new ButtonStore(); this.FioStore = new FioStore(); this.EmailStore = new EmailStore(); ... 


Sekarang App.js , kerangka aplikasi kita

 import React from "react"; import {observer, inject} from "mobx-react"; import ButtonArea from "./ButtonArea"; import Email from "./Email"; import Fio from "./Fio"; import l10n from "../../../l10n/localization.js"; @inject("mainStore") @observer export default class App extends React.Component { constructor(props) { super(props); }; render() { const mainStore = this.props.mainStore; return ( <div className="container"> <Fio label={l10n.ru.profile.name} name={"name"} value={mainStore.userData.name} daData={true} /> <Fio label={l10n.ru.profile.surname} name={"surname"} value={mainStore.userData.surname} daData={true} /> <Email label={l10n.ru.profile.email} name={"email"} value={mainStore.userData.email} /> <ButtonArea /> </div> ); } } 

Ada dua konsep Mobx lebih mendasar - menyuntikkan dan pengamat .
menyuntikkan hanya mengimplementasikan toko yang diperlukan dalam aplikasi. Bagian yang berbeda dari aplikasi kami menggunakan repositori yang berbeda, yang kami cantumkan dalam injeksi , dipisahkan dengan koma. Secara alami, toko-toko pluggable pada awalnya harus terdaftar di Penyedia . Repositori tersedia dalam komponen melalui this.props.yourStoreName .
pengamat - dekorator menunjukkan bahwa komponen kami akan berlangganan data yang dimodifikasi menggunakan Mobx. Data telah berubah - reaksi telah terjadi di komponen (akan ditampilkan di bawah). Dengan demikian, tidak ada langganan dan panggilan balik khusus - Mobx memberikan perubahan itu sendiri!

Kami akan kembali mengelola seluruh aplikasi di mainStore , tetapi untuk saat ini kami akan mengerjakan komponennya. Kami memiliki tiga jenis di antaranya - Fio , Email , Button . Biarkan yang pertama dan ketiga bersifat universal, dan Email - kustom. Mari kita mulai dengannya.

Layar adalah komponen React bodoh biasa:

Email.js
 import React from "react"; import {inject, observer} from 'mobx-react'; @inject("EmailStore") @observer export default class Email extends React.Component { constructor(props) { super(props); }; componentDidMount = () => { this.props.EmailStore.validate(this.props.name); }; componentWillUnmount = () => { this.props.EmailStore.unmount(this.props.name); }; render() { const name = this.props.name; const EmailStore = this.props.EmailStore; const params = EmailStore.params; let status = "form-group email "; if (params.isCorrect && params.onceValidated) status += "valid"; if (params.isWrong && params.onceValidated) status += "error"; return ( <div className={status}> <label htmlFor={name}>{this.props.label}</label> <input type="email" disabled={this.props.disabled} name={name} id={name} value={params.value} onChange={(e) => EmailStore.bindData(e, name)} /> </div> ); } } 

Kami menghubungkan komponen eksternal validasi, dan penting untuk melakukan ini setelah elemen sudah termasuk dalam tata letak. Oleh karena itu, metode dari toko disebut di componentDidMount .

Sekarang repositori itu sendiri:

EmailStore.js
 import {action, observable} from 'mobx'; import reactTriggerChange from "react-trigger-change"; import Validators from "../../../helpers/Validators"; import { getTarget } from "../../../helpers/elementaries"; export default class EmailStore { @observable params = { value : "", disabled : null, isCorrect : null, isWrong : null, onceValidated : null, prevalidated : null } /** *    */ @action bindData = (e, name) => { this.params.value = getTarget(e).value; }; /** *   */ @action validate = (name) => { const callbacks = { success : (formatedValue) => { this.params.value = formatedValue; this.params.isCorrect = true; this.params.isWrong = false; this.params.onceValidated = true; }, fail : (formatedValue) => { this.params.value = formatedValue; this.params.isCorrect = false; this.params.isWrong = true; } }; const options = { type : "email" }; const element = document.getElementById(name); new Validators(element, options, callbacks).init(); //    reactTriggerChange(element); this.params.prevalidated = true; }; } 


Perlu memperhatikan dua entitas baru.

diamati - objek, setiap perubahan bidang yang dimonitor oleh Mobx (dan mengirim sinyal ke pengamat , yang berlangganan penyimpanan khusus kami).
action - dekorator ini harus membungkus pawang yang mengubah keadaan aplikasi dan / atau menyebabkan efek samping. Di sini kita mengubah nilai nilai dalam params @observable .

Itu dia, komponen sederhana kami sudah siap! Dia dapat melacak data pengguna dan merekamnya. Nanti kita akan melihat bagaimana repositori pusat mainStore berlangganan untuk mengubah data ini.

Sekarang komponen Fio yang khas. Perbedaannya dari yang sebelumnya adalah bahwa kita akan menggunakan komponen jenis ini dalam jumlah tak terbatas dalam satu aplikasi. Ini membebankan beberapa persyaratan tambahan pada penyimpanan komponen. Selain itu, kami akan melakukan lebih banyak petunjuk tentang karakter input menggunakan layanan DaData yang sangat baik. Tampilan:

Fio.js
 import React from "react"; import {inject, observer} from 'mobx-react'; import {get} from 'mobx'; @inject("FioStore") @observer export default class Fio extends React.Component { constructor(props) { super(props); }; componentDidMount = () => { /** *       */ this.props.FioStore.registration(this.props); }; componentWillUnmount = () => { this.props.FioStore.unmount(this.props.name); }; render() { /** *       *  DaData: * data.surname -  * data.name -  * https://dadata.ru/api/suggest/name */ const FioStore = this.props.FioStore; const name = this.props.name; const item = get(FioStore.items, name); if (item && item.isCorrect && item.onceValidated && !item.prevalidated) status = "valid"; if (item && item.isWrong && item.onceValidated) status = "error"; //    store let value = this.props.value; if (item) value = item.value; return ( <div className="form-group fio"> <label htlmfor={name}>{this.props.label}</label> <input type="text" disabled={this.props.disabled} name={name} id={name} value={value} onChange={(e) => FioStore.bindData(e, name)} /> {(item && item.suggestions && item.suggestions.length > 0) && <div className="hint-container" id={"hint-container-" + item.id}>{item.suggestions.map((suggestion, i) => { return ( <div className={"suggestion-item fs-" + i} key={i} value={suggestion.data[name]} onClick={(e) => FioStore.setSuggestion(e, name)}> <span className="suggestion-text">{suggestion.data[name]}</span> </div>) })}</div>} </div> ); } } 

Ada sesuatu yang baru di sini: kita tidak mengakses status komponen secara langsung, tetapi melalui get :
 get(FioStore.items, name) 

Faktanya adalah bahwa jumlah instance komponen tidak terbatas, dan repositori adalah satu untuk semua komponen jenis ini. Oleh karena itu, saat pendaftaran, kami memasukkan parameter dari setiap instance di Peta :

Fiostore.js
 import {action, autorun, observable, get, set} from 'mobx'; import reactTriggerChange from "react-trigger-change"; import Validators from "../../../helpers/Validators"; import { getDaData, blockValidate } from "../../../helpers/functions"; import { getAttrValue, scrollToElement, getTarget } from "../../../helpers/elementaries"; export default class FioStore { constructor() { autorun(() => { /** *        .         setSuggestion() */ const self = this; $("body").click((e) => { if (e.target.className !== "suggestion-item" && e.target.className !== "suggestion-text") { const items = self.items.entries(); for (var [key, value] of items) { value.suggestions = []; } } }); }) } /** *   items       Fio    */ @observable items = new Map([]); /** *    */ @action registration = (params) => { const nameExists = get(this.items, params.name); if (!blockValidate({params, nameExists, type: "Fio"})) return false; //  items   const value = { value : params.value, disabled : params.disabled, isCorrect : null, isWrong : null, suggestions : [], daData : params.daData, startValidation : true, //      onceValidated : false, //      prevalidated : false }; set(this.items, params.name, value); this.validate(params.name); }; /** *    */ @action unmount = (name) => { this.items.delete(name); }; /** *    *    */ @action bindData = (e, name) => { const value = getTarget(e).value; const item = get(this.items, name); /** *     DaData */ if (item.daData && !item.startValidation) { getDaData({value, type: "fio", name}) .then((result) => { item.suggestions = result.suggestions; }) .catch((error) => {console.log(error)}) } else { item.startValidation = false; item.value = value; } }; /** *   */ @action setSuggestion = (e, name) => { if (e) e.preventDefault(); get(this.items, name).value = getAttrValue(e); //     get(this.items, name).suggestions = []; get(this.items, name).isCorrect = true; get(this.items, name).isWrong = false; }; /** *   */ @action validate = (name) => { const callbacks = { success : (formatedValue) => { get(this.items, name).value = formatedValue; get(this.items, name).isCorrect = true; get(this.items, name).isWrong = false; get(this.items, name).onceValidated = true; }, fail : (formatedValue) => { get(this.items, name).value = formatedValue; get(this.items, name).isCorrect = false; get(this.items, name).isWrong = true; } }; const options = { type : "fio" }; const element = document.getElementById(name); new Validators(element, options, callbacks).init(); //    reactTriggerChange(element); get(this.items, name).prevalidated = true; }; } 

Status komponen generik kami diinisialisasi sebagai berikut:

 @observable items = new Map([]); 

Akan lebih mudah untuk bekerja dengan objek JS biasa, namun, itu tidak akan "disadap" ketika mengubah nilai bidangnya, karena bidang ditambahkan secara dinamis ketika komponen baru ditambahkan ke halaman. Menerima petunjuk DaData yang kami ambil secara terpisah.

Komponen tombol terlihat serupa, tetapi tidak ada tips:

Button.js
 import React from "react"; import {inject, observer} from 'mobx-react'; @inject("ButtonStore") @observer export default class CustomButton extends React.Component { constructor(props) { super(props); }; componentDidMount = () => { /** *       */ this.props.ButtonStore.registration(this.props); }; componentWillUnmount = () => { this.props.ButtonStore.unmount(this.props.name); }; render() { const name = this.props.name; return ( <div className="form-group button"> <button disabled={this.props.disabled} onClick={(e) => this.props.ButtonStore.bindClick(e, name)} name={name} id={name} >{this.props.text}</button> </div> ); } } 


ButtonStore.js
 import {action, observable, get, set} from 'mobx'; import {blockValidate} from "../../../helpers/functions"; export default class ButtonStore { constructor() {} /** *   items       Button    */ @observable items = new Map([]) /** *    */ @action registration = (params) => { const nameExists = get(this.items, params.name); if (!blockValidate({params, nameExists, type: "Button"})) return false; //  items   const value = { disabled : params.disabled, isClicked : false }; set(this.items, params.name, value); }; /** *    */ @action unmount = (name) => { this.items.delete(name); }; /** *    */ @action bindClick = (e, name) => { e.preventDefault(); get(this.items, name).isClicked = true; }; } 


Komponen Button dibungkus oleh komponen HOC dari ButtonArea . Harap dicatat bahwa komponen yang lebih tua termasuk set toko sendiri, dan yang termuda. Dalam rantai komponen bersarang tidak perlu meneruskan parameter dan panggilan balik. Segala sesuatu yang diperlukan untuk pengoperasian komponen tertentu ditambahkan langsung padanya.

ButtonArea.js
 import React from "react"; import {inject, observer} from 'mobx-react'; import l10n from "../../../l10n/localization.js"; import Button from "./Button"; @inject("mainStore", "optionsStore") @observer export default class ButtonArea extends React.Component { constructor(props) { super(props); }; render() { return ( <div className="button-container"> <p>{this.props.optionsStore.dict.buttonsHeading}</p> <Button name={"send_data"} disabled={this.props.mainStore.buttons.sendData.disabled ? true : false} text={l10n.ru.common.continue} /> </div> ); } } 


Jadi, kami memiliki semua komponen yang siap. Masalahnya diserahkan kepada manajer mainStore. Pertama, semua kode penyimpanan:

mainStore.js
 import {observable, computed, autorun, reaction, get, action} from 'mobx'; import optionsStore from "./optionsStore"; import ButtonStore from "./ButtonStore"; import FioStore from "./FioStore"; import EmailStore from "./EmailStore"; import { fetchOrdinary, sendStats } from "../../../helpers/functions"; import l10n from "../../../l10n/localization.js"; class mainStore { constructor() { /** *    */ this.ButtonStore = new ButtonStore(); this.FioStore = new FioStore(); this.EmailStore = new EmailStore(); autorun(() => { this.fillBlocks(); this.fillData(); }); /** *      */ reaction( () => this.dataInput, (result) => { let isIncorrect = false; for (let i in result) { for (let j in result[i]) { const res = result[i][j]; if (!res.isCorrect) isIncorrect = true; this.userData[j] = res.value; } }; if (!isIncorrect) { this.buttons.sendData.disabled = false } else { this.buttons.sendData.disabled = true }; } ); /** *     */ reaction( () => this.sendDataButton, (result) => { if (result) { if (result.isClicked) { get(this.ButtonStore.items, "send_data").isClicked = false; const authRequestSuccess = () => { console.log("request is success!") }; const authRequestFail = () => { console.log("request is fail!") }; const request = { method : "send_userdata", params : { name : this.userData.name, surname : this.userData.surname, email : this.userData.email } }; console.log("Request body is:"); console.log(request); fetchOrdinary( optionsStore.OPTIONS.sendIdentUrl, JSON.stringify(request), { success: authRequestSuccess, fail: authRequestFail } ); } } } ); } @observable userData = { name : "", surname : "", email : "" }; @observable buttons = { sendData : { disabled : true } }; /** *     * @key -   * @value -      (name, type),          */ componentsMap = { userData : [ ["name", "fio"], ["surname", "fio"], ["email", "email"], ["send_data", "button"] ] }; /** *     listener'  stores */ @observable listenerBlocks = {}; /** *    */ @action fillBlocks = () => { for (let i in this.componentsMap) { const pageBlock = this.componentsMap[i]; //      (key)     (value) const blocks = {}; pageBlock.forEach((item, i) => { const _name = item[0]; const _type = item[1]; if (!blocks[_type]) { blocks[_type] = [_name] } else { blocks[_type].push(_name) } }) this.listenerBlocks[i] = blocks; } }; /** *    */ @action fillData = () => { if (optionsStore.preset) { // ,   undefined,     if (optionsStore.preset.name) this.userData.name = optionsStore.preset.name; if (optionsStore.preset.surname) this.userData.surname = optionsStore.preset.surname; } }; /** * Listener    */ @computed get dataInput() { const mappedResult = { fio : {}, email : { email : {} } }; if (this.FioStore && this.FioStore.items) { this.listenerBlocks.userData.fio.forEach((item) => { const i = get(this.FioStore.items, item); if (i) { mappedResult.fio[item] = { isCorrect : i.isCorrect, value : i.value } } }) } if (this.EmailStore && this.EmailStore.params) { mappedResult.email.email = { isCorrect : this.EmailStore.params.isCorrect, prevalidated : this.EmailStore.params.prevalidated, value : this.EmailStore.params.value } } return mappedResult } /** * Listener     */ @computed get sendDataButton() { let result = {}; const i = get(this.ButtonStore.items, "send_data"); if (i) { result = { isClicked : i.isClicked } } return result } } export default new mainStore(); 


Beberapa entitas kunci lagi.

dihitung adalah dekorator untuk fungsi yang melacak perubahan yang dapat diamati . Keuntungan penting Mobx adalah hanya melacak data yang dikomputasi dan kemudian dikembalikan sebagai hasilnya. Reaksi dan, sebagai konsekuensinya, penggambaran ulang DOM virual terjadi hanya jika diperlukan.
reaksi - alat untuk mengatur efek samping berdasarkan kondisi yang berubah. Dibutuhkan dua fungsi: yang pertama dihitung, mengembalikan keadaan yang dihitung, yang kedua dengan efek yang harus mengikuti perubahan keadaan. Dalam contoh kita, reaksi diterapkan dua kali. Dalam yang pertama, kita melihat keadaan bidang dan menyimpulkan apakah seluruh formulir sudah benar, dan juga mencatat nilai masing-masing bidang. Pada yang kedua, kita klik tombol (lebih tepatnya, jika ada tanda "tombol ditekan"), kami mengirim data ke server. Objek data ditampilkan di konsol browser. Karena mainStore mengetahui semua repositori, segera setelah memproses klik tombol, kami dapat menonaktifkan bendera dengan gaya imperatif:

 get(this.ButtonStore.items, "send_data").isClicked = false; 

Anda dapat mendiskusikan seberapa dapat diterimanya kehadiran "imperatif" tersebut, tetapi dalam hal apa pun, kontrol hanya terjadi dalam satu arah - dari mainStore ke ButtonStore .
autorun digunakan di mana kami ingin menjalankan beberapa tindakan secara langsung, bukan sebagai reaksi untuk menyimpan perubahan. Dalam contoh kami, satu fungsi bantu diluncurkan, serta prefilling bidang formulir dengan data dari kamus.

Dengan demikian, urutan tindakan yang kita miliki adalah sebagai berikut. Komponen melacak peristiwa pengguna dan mengubah statusnya. mainStore melalui dihitung menghitung hasil hanya berdasarkan negara yang telah berubah. Yang berbeda dihitung mencari perubahan di berbagai negara di repositori yang berbeda. Selanjutnya, melalui reaksi, berdasarkan hasil yang dihitung , kami melakukan tindakan dengan dapat diamati , serta melakukan efek samping (misalnya, kami membuat permintaan AJAX). Obsevables berlangganan komponen anak, yang digambar ulang jika perlu. Aliran data searah dengan kontrol penuh atas di mana dan apa yang berubah.

Anda dapat mencoba contoh dan kodenya sendiri. Tautan ke repositori: github.com/botyaslonim/mobx-habr .
Kemudian seperti biasa: npm i , npm run local . Di folder publik , file index.html . Petunjuk DaData berfungsi pada akun gratis saya, oleh karena itu, mereka mungkin dapat jatuh pada beberapa titik karena efek habr.

Saya akan dengan senang hati memberikan komentar dan saran yang membangun tentang pekerjaan aplikasi di Mobx!

Sebagai kesimpulan, saya akan mengatakan bahwa perpustakaan telah sangat menyederhanakan pekerjaan dengan data. Untuk aplikasi kecil dan menengah, itu pasti akan menjadi alat yang sangat nyaman untuk melupakan sifat-sifat komponen dan panggilan balik dan berkonsentrasi langsung pada logika bisnis.

Source: https://habr.com/ru/post/id471048/


All Articles