前面介绍许多 State Machine 及 XState 的功能,由於篇幅不多了,今天想跟大家先快速的介绍一下在 React 中如何使用 XState。
快速小回顾一下我们之前学到的一些 API
本篇范例程序码( example code )皆出自 XState
import { createMachine, interpret } from 'xstate';
const toggleMachineConfig = {
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
};
// createMachine API 把 config、definition 建立成一个 pure function 的状态机
// 让 interpreter 可以使用
const toggleMachine = createMachine(toggleMachineConfig);
// interpret API 把 state machine 建立成一个 instance、service 供现实应用
// 比如像监听 状态的转移、记忆当前状态
const toggleService = interpret(toggleMachine)
.onTransition((state) => console.log(state.value))
.start();
// => 'inactive'
// 比如像 发送事件
toggleService.send('TOGGLE');
// => 'active'
toggleService.send('TOGGLE');
useMachine 可以在这个 Component 的生命周期里,将我们经过 createMachine 的一台 FSM 启动成为一个服务。
可以从 useMachine 回传的一组 Tuple ,拿到下面 3 个东西
state.value
拿到 当前 state;state.matches('stateName')
回传 true
/ false
告诉我们当前是不是我们期待的 statesend('eventName')
可以在该 service 发送事件import { useMachine } from '@xstate/react';
import { toggleMachine } from '../path/to/toggleMachine';
function Toggle() {
const [state, send, service] = useMachine(toggleMachine, {...someExtraOptions});
// someExtraOptions 就像是我们之前学过得挂载 Actions 跟 Guards
return (
<button onClick={() => send('TOGGLE')}>
{state.matches('inactive') ? 'Off' : 'On'}
</button>
);
}
useMachine 像是 useState 、 useEffect ,常被使用於 Component 的 Local State!
有时候,我们也会想将这个 Machine Instance 作为 props 传给子元件使用,此时底下的子元件就需要透过 useService 及 useActor 来使用这个被建立好的 instance
import { useMachine } from '@xstate/react';
import { toggleMachine } from '../path/to/toggleMachine';
function ParentComponent() {
const [state, send, service] = useMachine(toggleMachine);
return (
<div>
<ChildComponent serivce={service} />
</div>
);
};
由於 Service 也是 Actor ,useService 的 API 将会在 V5 被弃用,原本透过 send event 额外带的资料,方式也从 send('TOGGLE', extraData)
改为 send({ type: 'TOGGLE', ...extraData })
!
import { useActor, useService } from '@xstate/react';
function ChildComponent({service}) {
- const [state, send] = useService(service); // V5 将被弃用
+ const [state, send] = useActor(service);
+ const extraData = { x:0,y:1 };
return (
- <button onClick={() => send('TOGGLE', extraData)}>
+ <button onClick={() => send({ type: 'TOGGLE', ...extraData })}>
{state.matches('inactive') ? 'Off' : 'On'}
</button>
);
}
如果我们想要在某个 transition 前後执行 side effect 时,前面我们知道要使用 action ,并在里面订定 side effect 要做什麽,但假如今天 我们的 side effect 是想要与 React 元件互动的话...
XState 也提供 asEffect 这个 API,当 transition 发生时,action 不会马上被执行,他会被作为 useEffect 里的 side effect 被执行。
类似API 如 useLayoutEffect 也有对应的 asLayoutEffect
const machine = createMachine({
initial: 'focused',
states: {
focused: {
entry: 'focus'
}
}
});
const Input = () => {
const inputRef = useRef(null);
const [state, send] = useMachine(machine, {
actions: {
focus: asEffect((context, event) => {
inputRef.current && inputRef.current.focus();
})
}
});
return <input ref={inputRef} />;
};
比如点了某个 Toggle button 进入状态转换,转换到下个状态前,想要用 React Router 导向其他页面。
我们先在 Machine Config 定义 action 的名称 goToOtherPage
import { createMachine } from 'xstate';
export const machine = createMachine({
initial: 'toggledOff',
states: {
toggledOff: {
on: {
TOGGLE: 'toggledOn'
}
},
toggledOn: {
entry: ['goToOtherPage']
}
}
});
再将 goToOtherPage
的实作放在 React 元件里
import { machine } from './machine';
import { useMachine } from '@xstate/react';
import { useHistory } from 'react-router';
const Component = () => {
const history = useHistory();
const [state, send] = useMachine(machine, {
actions: {
goToOtherPage: () => {
history.push('/other-page');
}
}
});
return null;
};
有时我们进行 AJAX 获取资料,像使用 SWR, React-Query 等,会非同步向外部取得资料。
此时我们可以藉由 useEffect 当资料发生改变时,就打出一个 send Event ,将新的资料传入 machine instance 里。
const Component = () => {
const { data, error } = useSWR('/api/user', fetcher);
const [state, send] = useMachine(machine);
useEffect(() => {
send({
type: 'DATA_CHANGED',
data,
error
});
}, [data, error, send]);
};
也可以透过 switch / case
、if / else
、三元运算式state==='some'? <A /> : <B />
,搭配 XState 的 State Object
可以使用 state.value 或 state.matches('someState')
switch (state.value) {
case 'idle':
return (
<button onClick={() => send('FETCH', { query: 'something' })}>
Search for something
</button>
);
case 'loading':
return <div>Searching...</div>;
case 'success':
return <div>Success! Data: {state.context.data}</div>;
case 'failure':
return (
<>
<p>{state.context.error.message}</p>
<button onClick={() => send('RETRY')}>Retry</button>
</>
);
default:
return null;
}
https://xstate.js.org/docs/recipes/react.html
https://xstate.js.org/docs/packages/xstate-react/
=x= 🌵 NEWS 前台 View 页面後端功能制作。 NEWS View 页面资料介绍 : 📌 ...
阿嬷都看得懂的盒模型 各位阿嬷,我们今天要来寄自己腌渍的酱瓜给乖孙。 我们找来 4 个纸盒,想在里面...
一般来说,ARM组合语言,目前大部分是称作嵌入式系统,大部分会说它是写到韧体里面的程序语言,就是硬体...
这系列的程序码在 https://github.com/DanSnow/ironman-2020/...
流程指派这件事情,还会依据流程关卡不同,有不同方向走法 像下图用自动节点指向两种路线 同样套用程序J...