Fetch data
GET api/users
Pending
200 VALE
Fetch data
GET api/users
Cargando...
200 VALE
Fetch data
GET api/users
Cargando
GET api/users
Cargando
GET api/users
Cargando
GET api/users
Cargando
GET api/users
QuΓ© narices
Fetch data
GET api/users
Cargando
200 VALE
Fetch data
GET api/users
Cargando
GET api/users
Cargando
GET api/users
Cargando
GET api/users
Cargando
Fetch data
π
π
π
π
Delete
Are you sure?
Yes
No
Delete
Undo
Deleted!
have one initial state
a finite number of states
a finite number of events
a mapping of state transitionsΒ
triggered by events
a finite number of final states
Sign in
Signing in...
Profile
Home
SUBMIT
SUCCESS
UNAUTHORIZED
PROFILE
Idle
Searching...
entry / prefetchResources
entry / fetchResults
Search
[query.length > 0]
H
Idle
Searching...
SEARCH
Success
Failure
RESOLVE
REJECT
SEARCH
Searched
Idle
Searching...
SEARCH
Success
Failure
RESOLVE
REJECT
SEARCH
Searched
const machine = Machine({
initial: 'idle',
states: {
// ...
searching: {
on: {
RESOLVE: 'searched',
REJECT: 'searched.failure'
}
},
searched: {
initial: 'success',
states: {
success: {},
failure: {}
},
on: { SEARCH: 'searching' }
}
}
});
How was your experience?
π
Good
Bad
Tell us why?
π
Submit
Thanks for your feedback.
π
Close
Define transitions between
states & events
function searchReducer(state = 'idle', event) {
switch (state) {
case 'idle':
switch (event.type) {
case 'SEARCH':
return 'searching';
default:
return state;
}
case 'searching':
switch (event.type) {
case 'RESOLVE':
return 'success';
case 'REJECT':
return 'failure';
default:
return state;
}
case 'success':
switch (event.type) {
case 'SEARCH':
return 'searching';
default:
return state;
}
case 'failure':
switch (event.type) {
case 'SEARCH':
return 'searching';
default:
return state;
}
default:
return state;
}
}
const machine = {
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
CLICK_GOOD: 'thanks',
CLICK_BAD: 'form',
CLOSE: 'closed',
ESC: 'closed'
}
},
form: {
on: {
SUBMIT: 'thanks',
CLOSE: 'closed',
ESC: 'closed'
}
},
thanks: {
on: {
CLOSE: 'closed',
ESC: 'closed'
}
},
closed: {
type: 'final'
}
}
};
Define transitions between
states & events
const transition = (state, event) => {
return machine
.states[state] // current state
.on[event] // next state
|| state; // or same state
}
import { Machine } from 'xstate';
const feedbackMachine = Machine({
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
CLICK_GOOD: 'thanks',
CLICK_BAD: 'form',
CLOSE: 'closed',
ESC: 'closed'
}
},
form: {
on: {
SUBMIT: 'thanks',
CLOSE: 'closed',
ESC: 'closed'
}
},
thanks: {
on: {
CLOSE: 'closed',
ESC: 'closed'
}
},
closed: {
type: 'final'
}
}
});
npm install xstate
import { feedbackMachine } from './feedbackMachine';
import { useMachine } from '@xstate/react';
const App = () => {
// const [state, dispatch] = useReducer(feedbackReducer, 'question');
const [current, send] = useMachine(feedbackMachine);
if (current.matches('question')) {
return (
<QuestionScreen
onClickGood={() => send({ type: 'GOOD' })}
onClickBad={() => send({ type: 'BAD' })}
onClose={() => send({ type: 'CLOSE' })}
/>
);
} else if (/* ... */) {
// ...
}
}
Photo by NEW DATA SERVICESΒ on Unsplash
Success
Signing in
Error
0.9
0.1
How was your experience?
π
Good
Bad
Tell us why?
π
Submit
Thanks for your feedback.
π
Close
Give Feedback
β
β
Login
Gallery
Profile
Camera
SUCCESS
TAP PROFILE
TAP CAMERA
BACK
BACK
Scroll
πΊ Login, Gallery, Gallery
πΊ Login, Gallery, Camera
πΊ Login, Gallery, Profile
0.90, 0.31
0.90, 0.57
0.90, 0.12
0.90
0.31
0.57
0.13
UNAUTHORIZED
0.10
SUCCESS
(decide)
TAP AD
+10
Gallery
Ads interspersed
Ads shown at top
Ad
Scroll
Scroll
TAP AD
+10
-1
-10
INTERSPERSED > .5
TOP > .5
Contextual data
New user?
< 30 days
>= 30 days
Show ad at top
Friend count?
< 100
>= 100
Show more ads
Show less ads
All data
A
B
C
D
E
A
B
C
D
E
import { Machine } from 'xstate';
const myMachine = Machine({
// ...
});
const myService = interpret(myMachine)
.onTransition(state => {
analytics.track({
target: state.value,
source: state.history
? state.history.value
: undefined,
event: sanitize(event),
timestamp: Date.now()
});
})
.start();
A
B
C
D
E
import { Machine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';
const myMachine = Machine({
// ...
});
const shortestPaths = getShortestPaths(myMachine);
A: A
B: A -> B
C: A -> B -> C
D: A -> D
E: A -> D -> E
A
B
C
D
E
import { Machine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';
const myMachine = Machine({
// ...
});
const shortestPaths = getShortestPaths(myMachine);
0.9
1.0
0.4
0.6
0.1
0.4
0.6
A: A
B: A -> D -> E -> B
C: A -> D -> E -> C
D: A -> D
E: A -> D -> E
A
B
C
0.75
0.25
D
E
(D) 0.95
(D) 0.05
A -> B A -> C A -> B A -> B
E -> D -> E -> F ->
Agent
Environment
Action
State
Reward
service.onTransition(state => {
analytics.track({
source: state.history.value,
target: state.value,
context: state.context,
event: state.event,
timestamp: Date.now()
})
});
Application
Executable Model
Metrics Tracking
Adaptive Transitions
Adaptive UI
Statecharts
FSMs
Bottom-up
States & logic
Code Complexity
xstate
@xstate/react
@xstate/graph
@xstate/vue
@xstate/test
@xstate/analytics
@xstate/sim
@xstate/viz
β
β
β
π
π
π
π
β