如何在React组件的useEffect中设置xstate状态机的状态
React XState 审批状态组件实现方案
功能实现说明
针对需求做了以下核心修改:
- 组件挂载时通过
useEffect拉取服务端存储的当前状态,同步到XState状态机 - 封装状态切换逻辑,点击按钮先调用持久化接口,请求成功后再触发XState状态变更
- 新增加载状态,避免接口请求过程中重复点击
完整实现代码
import React, { useEffect, useState } from 'react'; import { useParams, useHistory } from "react-router-dom"; import { createMachine, assign } from 'xstate'; import { useMachine } from "@xstate/react"; import {MagellanButton} from "./Styles"; import 'bootstrap/dist/css/bootstrap.min.css'; import '../App.css'; const approvalMachine = createMachine({ id: 'approve', initial: 'loading', // 新增初始化加载状态 context: { retries: 0, currentState: null }, states: { loading: { on: { // 初始化同步状态事件 INIT: { target: (context, event) => event.initialState, actions: assign({ currentState: (_, event) => event.initialState }) } } }, 'Not Submitted': { on: { SUBMIT: 'Pending Approval' } }, 'Pending Approval': { on: { CANCEL: 'Not Submitted', CHANGE: 'Change Request', DENIED: 'Denied', APPROVED: 'Approved' } }, 'Change Request': { on: { RESUBMITTED: 'Pending Approval', CANCEL: 'Not Submitted' } }, Denied: { type: 'final' }, Approved: { on: { PUBLISH: 'Published' } }, Published: { type: "final" } } }); function MagellanStateManager({id}) { const parameters = useParams(); const history = useHistory() const [state, send] = useMachine(approvalMachine); const [isLoading, setIsLoading] = useState(false); // 组件初始化拉取服务端状态 useEffect(() => { const fetchState = async () => { try { // 替换为实际的状态查询接口地址 const res = await fetch(`/api/approval/${id}/state`); const data = await res.json(); // 将服务端返回的状态同步到状态机 send({ type: 'INIT', initialState: data.state // 假设接口返回字段为state,值为状态机定义的状态名 }); } catch (err) { console.error('获取状态失败:', err); // 可根据需求添加错误提示、默认状态设置逻辑 send({type: 'INIT', initialState: 'Not Submitted'}); } }; fetchState(); }, [id, send]); // 封装状态切换持久化逻辑 const handleStateChange = async (eventType) => { if (isLoading) return; setIsLoading(true); try { // 计算切换后的目标状态 const targetState = approvalMachine.transition(state.value, eventType).value; // 替换为实际的状态持久化接口地址 await fetch(`/api/approval/${id}/state`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ state: targetState }) }); // 接口调用成功后触发状态机状态变更 send(eventType); } catch (err) { console.error('状态更新失败:', err); // 可根据需求添加错误提示逻辑 } finally { setIsLoading(false); } }; return ( <span style={{float: "right", marginTop: 8}}> <span className="m-form-label ml-3">State:</span> <span>{state.value === 'loading' ? '加载中...' : state.value}</span> <MagellanButton className="ml-3" disabled={isLoading || !state.nextEvents.includes('SUBMIT')} onClick={() => handleStateChange('SUBMIT')}>Submit</MagellanButton> <MagellanButton className="ml-3" disabled={isLoading || !state.nextEvents.includes('CANCEL')} onClick={() => handleStateChange('CANCEL')}>Cancel</MagellanButton> <MagellanButton className="ml-3" disabled={isLoading || !state.nextEvents.includes('CHANGE')} onClick={() => handleStateChange('CHANGE')}>Change</MagellanButton> <MagellanButton className="ml-3" disabled={isLoading || !state.nextEvents.includes('RESUBMITTED')} onClick={() => handleStateChange('RESUBMITTED')}>Resubmit</MagellanButton> <MagellanButton className="ml-3" disabled={isLoading || !state.nextEvents.includes('DENIED')} onClick={() => handleStateChange('DENIED')}>Deny</MagellanButton> <MagellanButton className="ml-3" disabled={isLoading || !state.nextEvents.includes('APPROVED')} onClick={() => handleStateChange('APPROVED')}>Approve</MagellanButton> <MagellanButton className="ml-3" disabled={isLoading || !state.nextEvents.includes('PUBLISH')} onClick={() => handleStateChange('PUBLISH')}>Publish</MagellanButton> </span> ) } export default MagellanStateManager;
关键改动说明
- 新增了
loading初始化状态,避免初始状态未加载完成时展示错误状态和允许误操作 - 封装了
handleStateChange统一处理按钮点击逻辑,先调用持久化接口,成功后再同步状态机状态,保证前后端状态一致 - 添加了
isLoading状态,防止接口请求过程中用户重复点击触发多次请求 - 代码中接口地址为示例值,可根据实际业务替换为自身项目的接口地址,同时调整请求参数、返回值解析逻辑即可
内容的提问来源于stack exchange,提问作者Loren Cahlander
相关产品推荐
相关产品推荐

