مثال صغير لاستخدام مكتبة XState بواسطة David Khourshid لوصف منطق مكون VueJS 2. XState هي مكتبة متقدمة جدًا لإنشاء واستخدام آلات الحالة في JS. ليست مساعدة سيئة في المهمة الصعبة المتمثلة في إنشاء تطبيقات الويب.
عصور ما قبل التاريخ
في مقالتي الأخيرة ، وصفت بإيجاز سبب الحاجة إلى آلات الحالة (آلات الحالة) وتنفيذ بسيط للعمل مع Vue. دراجتي تحتوي على حالات فقط وبدا إعلان الدولة كما يلي:
{
idle: ['waitingConfirmation'],
waitingConfirmation: ['idle','waitingData'],
waitingData: ['dataReady', 'dataProblem'],
dataReady: [‘idle’],
dataProblem: ['idle']
}
في الواقع ، كان تعدادًا للحالات ، ولكل مجموعة من الحالات المحتملة التي يمكن للنظام الانتقال إليها تم وصفها. التطبيق ببساطة "يقول" لجهاز الحالة - أريد الدخول في مثل هذه الحالة ، إذا أمكن ، ينتقل الجهاز إلى الحالة المطلوبة.
هذا النهج يعمل ، ولكن هناك مضايقات. على سبيل المثال ، إذا كان الزر في حالة مختلفة يجب أن يبدأ الانتقال إلى حالات مختلفة. سيتعين علينا إقامة سياج في الظروف. بدلاً من التصريح ، نحصل على العصيدة.
بعد دراسة النظرية على مقاطع الفيديو من YouTube ، أصبح من الواضح أن الأحداث ضرورية ومهمة. ولد هذا النوع من التصريحات في رأسي:
{
idle: {
GET: 'waitingConfirmation',
},
waitingConfirmation: {
CANCEL: 'idle',
CONFIRM: 'waitingData'
},
waitingData: {
SUCCESS: 'dataReady',
FAILURE: 'dataProblem'
},
dataReady: {
REPEAT: 'idle'
},
dataProblem: {
REPEAT: 'idle'
}
}
وهذا بالفعل مشابه جدًا لكيفية وصف مكتبة XState للحالات. بعد قراءة قفص الاتهام بعناية أكبر ، قررت وضع دراجتي محلية الصنع في الحظيرة والتحول إلى واحدة تحمل علامة تجارية.
VUE + XState
التثبيت بسيط للغاية ، اقرأ المستند ، بعد التثبيت نقوم بتضمين XState في المكون:
import {Machine, interpret} from ‘xstate’
نقوم بإنشاء سيارة بناءً على كائن التصريح:
const myMachine = Machine({
id: 'myMachineID',
context: {
/* some data */
},
initial: 'idle',
states: {
idle: {
on: {
GET: 'waitingConfirmation',
}
},
waitingConfirmation: {
on: {
CANCEL: 'idle',
CONFIRM: 'waitingData'
}
},
waitingData: {
on: {
SUCCESS: 'dataReady',
FAILURE: 'dataProblem'
},
},
dataReady: {
on: {
REPEAT: 'idle'
}
},
dataProblem: {
on: {
REPEAT: 'idle'
}
}
}
})
من الواضح أن هناك حالات "خاملة" و "انتظار تأكيد" ... وهناك أحداث بأحرف كبيرة GET و CANCEL و CONFIRM….
الجهاز نفسه لا يعمل ، تحتاج إلى إنشاء خدمة منه باستخدام وظيفة الترجمة. سنضع رابطًا لهذه الخدمة في ولايتنا ، وفي نفس الوقت رابط إلى الحالة الحالية:
data: {
toggleService: interpret(myMachine),
current: myMachine.initialState,
}
يجب أن تبدأ الخدمة - ابدأ () ، ونشير أيضًا إلى أنه عندما تنتقل الحالة ، نقوم بتحديث القيمة الحالية:
mounted() {
this.toggleService
.onTransition(state => {
this.current = state
})
.start();
}
نضيف وظيفة الإرسال إلى الطرق ونستخدمها للتحكم في الجهاز - لإرسال الأحداث إليه:
methods: {
send(event) {
this.toggleService.send(event);
},
…
}
حسنًا ، كل شيء بسيط. أرسل حدثًا ببساطة عن طريق الاتصال بـ:
this.send(‘SUCCESS’)
اكتشف الوضع الحالي:
this.current.value
تحقق مما إذا كان الجهاز في حالة معينة كما يلي:
this.current.matches(‘waitingData')
ضع كل شيء معا:
قالب
<div id="app">
<h2>XState machine with Vue</h2>
<div class="panel">
<div v-if="current.matches('idle')">
<button @click="send('GET')">
<span>Get data</span>
</button>
</div>
<div v-if="current.matches('waitingConfirmation')">
<button @click="send('CANCEL')">
<span>Cancel</span>
</button>
<button @click="getData">
<span>Confirm get data</span>
</button>
</div>
<div v-if="current.matches('waitingData')" class="blink_me">
loading ...
</div>
<div v-if="current.matches('dataReady')">
<div class='data-hoder'>
{{ text }}
</div>
<div>
<button @click="send('REPEAT')">
<span>Back</span>
</button>
</div>
</div>
<div v-if="current.matches('dataProblem')">
<div class='data-hoder'>
Data error!
</div>
<div>
<button @click="send('REPEAT')">
<span>Back</span>
</button>
</div>
</div>
</div>
<div class="state">
Current state: <span class="state-value">{{ current.value }}</span>
</div>
</div>
شبيبة
const { Machine, interpret } = XState
const myMachine = Machine({
id: 'myMachineID',
context: {
/* some data */
},
initial: 'idle',
states: {
idle: {
on: {
GET: 'waitingConfirmation',
}
},
waitingConfirmation: {
on: {
CANCEL: 'idle',
CONFIRM: 'waitingData'
}
},
waitingData: {
on: {
SUCCESS: 'dataReady',
FAILURE: 'dataProblem'
},
},
dataReady: {
on: {
REPEAT: 'idle'
}
},
dataProblem: {
on: {
REPEAT: 'idle'
}
}
}
})
new Vue({
el: "#app",
data: {
text: '',
toggleService: interpret(myMachine),
current: myMachine.initialState,
},
computed: {
},
mounted() {
this.toggleService
.onTransition(state => {
this.current = state
})
.start();
},
methods: {
send(event) {
this.toggleService.send(event);
},
getData() {
this.send('CONFIRM')
requestMock()
.then((data) => {
this.text = data.text
this.send('SUCCESS')
})
.catch(() => this.send('FAILURE'))
},
}
})
function randomInteger(min, max) {
let rand = min + Math.random() * (max + 1 - min)
return Math.floor(rand);
}
function requestMock() {
return new Promise((resolve, reject) => {
const randomValue = randomInteger(1,2)
if(randomValue === 2) {
let data = { text: 'Data received!!!'}
setTimeout(resolve, 3000, data)
}
else {
setTimeout(reject, 3000)
}
})
}
وبالطبع كل هذا يمكن لمسه على jsfiddle.net
متخيل
يوفر XState أداة رائعة ، Visualizer . يمكنك رؤية الرسم التخطيطي لسيارتك الخاصة. وليس فقط للبحث ولكن أيضًا للنقر على الأحداث وإجراء انتقالات. هكذا يبدو مثالنا:
النتيجة
يعمل XState بشكل رائع مع VueJS. هذا يبسط عمل المكون ويسمح لك بالتخلص من التعليمات البرمجية غير الضرورية. الشيء الرئيسي هو أن إعلان الجهاز يتيح لك فهم المنطق بسرعة. هذا المثال بسيط ، لكنني جربته بالفعل على مثال أكثر تعقيدًا لمشروع عمل. الرحلة طبيعية.
في هذه المقالة ، استخدمت فقط الوظائف الأساسية للمكتبة ، حيث لا يزال لدي ما يكفي منها ، لكن المكتبة تحتوي على العديد من الميزات المثيرة للاهتمام:
- انتقالات حراسة
- الإجراءات (دخول ، خروج ، انتقال)
- الحالة الممتدة (السياق)
- الدول المتعامدة (الموازية)
- حالات هرمية (متداخلة)
- التاريخ
وهناك أيضًا مكتبات مماثلة ، على سبيل المثال Robot. فيما يلي مقارنة بين أجهزة الحالة: XState مقابل. روبوت . لذلك إذا كنت مهتمًا بموضوع ما ، فسيكون لديك ما تفعله.