-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStateMachinePseudoApi.js
127 lines (118 loc) · 4.76 KB
/
StateMachinePseudoApi.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
let machStack = [];
class Machine{
constructor(machFields){
this.machFields = machFields;
this.curState = machFields.initialState;
}
toAction(event, action) {
if (typeof action === 'string') {
this.toAction(event, this.machFields.actions[action]);
} else if (typeof action === 'function') {
machStack.push({machine: this, event: event});
action(event);
machStack.pop();
} else if (Array.isArray(action)) {
for (let i = 0; i < action.length; i++) {
this.toAction(event, action[i]);
}
}
}
transition(transaction, event) {
const transition = this.machFields.states[this.curState].on[transaction];
this.toAction(event, transition.service || (() => {
const [machCurSt, setState] = useState();
setState(transition.target);
}));
}
}
const machine = function(machFields) {
return new Machine(machFields);
};
function useState() {
const {machine, event} = {...machStack[machStack.length - 1]};
if (!machine) {
throw new Error("useState() should be run from state machine");
}
const setState = function (newState) {
const onExit = machine.machFields.states[machine.curState].onExit;
machine.toAction(event, onExit);
machine.curState = newState;
const onEntry = machine.machFields.states[machine.curState].onEntry;
machine.toAction(event, onEntry);
};
return [machine.curState, setState];
}
function useContext() {
const {machine, event} = {...machStack[machStack.length - 1]};
if (!machine) {
throw new Error("useContext() should be run from state machine");
}
const setContext = function (newContext) {
machine.machFields.context = {...machine.machFields.context, ...newContext};
};
return [machine.machFields.context, setContext];
}
// machine — создает инстанс state machine (фабрика)
const vacancyMachine = machine({
// У каждого может быть свой id
id: 'vacancy',
// начальное состояние
initialState: 'notResponded',
// дополнительный контекст (payload)
context: {id: 123},
// Граф состояний и переходов между ними
states: {
// Каждое поле — это возможное состоение
responded: {
// action, который нужно выполнить при входе в это состояние. Можно задавать массивом, строкой или функцией
onEntry: 'onStateEntry'
},
notResponded: {
// action, который нужно выполнить при выходе из этого состояния. Можно задавать массивом, строкой или функцией
onExit() {
console.log('we are leaving notResponded state');
},
// Блок описания транзакций
on: {
// Транзакция
RESPOND: {
// упрощенный сервис, вызываем при транзакции
service: (event) => {
// Позволяет получить текущий контекст и изменить его
const [contex, setContext] = useContext();
// Позволяет получить текущий стейт и изменить его
const [state, setState] = useState();
// // Поддерживаются асинхронные действия
// window.fetch({method: 'post', data: {resume: event.resume, vacancyId: context.id} })
// .then(() => {
// // меняем состояние
// setState('responded');
// // Мержим контекст
// setContext({completed: true}); // {id: 123, comleted: true}
// });
// меняем состояние
setState('responded');
// Мержим контекст
setContext({completed: true}); // {id: 123, comleted: true}
},
// Если не задан сервис, то просто переводим в заданный target, иначе выполняем сервис.
target: 'responded',
}
}
},
},
// Раздел описание экшенов
actions: {
onStateEntry: (event) => {
const [state] = useState();
console.log('now state is ' + state);
},
makeResponse: (event) => {
// both sync and async actions
const [context, setContext] = useContext();
window.fetch({method: 'post', data: {resume: event.resume, vacancyId: context.id} });
}
}
});
// Пример использования StateMachine
vacancyMachine.transition('RESPOND', {resume: {name: 'Vasya', lastName: 'Pupkin'}});