有没有办法使用从 Firebase 返回的承诺来初始化 Xstate 状态机?
Posted
技术标签:
【中文标题】有没有办法使用从 Firebase 返回的承诺来初始化 Xstate 状态机?【英文标题】:Is there a way to initialize an Xstate state machine using a promise returned from Firebase? 【发布时间】:2021-06-07 00:00:54 【问题描述】:我正在尝试使用 Firebase 保持状态。我目前正在使用“saveState”功能,该功能可以正常工作并将最新状态正确保存到 Firebase。
现在我希望能够根据 Firebase 中最近保存的状态来初始化状态机。在下面的代码中,我尝试使用我的“loadState”函数为 Xstate 提供一个配置对象。它目前返回一个带有正确状态配置的承诺。
这是我的“保存状态”代码:
//This function works fine.
function saveState(current, id)
let transactionJSON = serialize(current);
transactionJSON['createdOn'] = new Date();
return firebase.saveTransactionState(transactionJSON, transactionId:id);
这是我的“loadState”函数,它从 Firebase 返回一个带有正确配置信息的承诺。
function loadState(id)
return firebase.getTransactionState(transactionId:id).then(function(querySnapshot)
return querySnapshot.docs.map(doc => deserialize(...doc.data()) );
);
;
现在我的问题是尝试使用上述“loadState”函数加载 Xstate。在这里,我尝试使用 useMachine React 钩子:
const [current, send] = useMachine(transactionMachine,
state: () => loadState(id), //Trying to do something like this, but it doesn't seem to work.
actions:
save: () => saveState(current, id),
,
);
我最终得到错误:“TypeError: Cannot read property 'data' of undefined”,我认为这是因为承诺尚未解决,导致尝试读取未定义的值。
这有可能吗,还是我做错了?
我对这一切都很陌生,任何指导将不胜感激。谢谢。
【问题讨论】:
这里一样,谁知道答案 您无法使用异步获取的值初始化状态,因为 javascript 是单线程的,因此如果您以某种方式等待该值从 Firebase 返回,您的整个应用程序将完全冻结,直到它返回- 不好。只需提供某种虚假或默认的初始状态并在您的 React 代码中处理它,然后渲染加载微调器/消息,或者只是根据数据是否已获取来有条件地渲染组件。 @Jayce444,是的,感谢您的评论。我只是认为使用 Xstate 反应钩子可能有一些很酷的方法(因为我可能没有正确理解他们的文档),好像 Promise 本身就是状态机。 【参考方案1】:我想您可以使用useEffect
在获取后更新您的状态机。
在状态机中调用 fetch 怎么样?
import Machine, assign from 'xstate';
const yourMachine = Machine(
id: 'yourStateMachine',
initial: 'fetching',
context:
values:
id: '', // likely need a state to initialize this value before fetching
apiDat:
,
,
states:
fetching:
invoke:
src: (values) => firebase
.getTransactionState( transactionId: values.id )
.then(function(querySnapshot)
return querySnapshot.docs.map(
doc => deserialize(...doc.data())
);
),
onDone: target: 'resolving', actions: 'cacheApiDat' ,
onError: target: 'error.fetchFailed' ,
,
resolving:
initial: 'delay',
states:
delay:
after:
750: 'resolve'
,
,
resolve:
always: [
cond: 'isSuccess', target: '#yourStateMachine.idle' ,
cond: 'isUnauthorized', target: '#yourStateMachine.error.auth' ,
],
,
,
error:
states:
fetchFailed:
type: 'final',
,
auth:
type: 'final',
,
idle:
,
,
actions:
cacheApiDat: assign( values: (values, event) => (
...values,
apiDat: event.data, // save whatever values you need here
),
),
,
guards:
// this requires a definition based on firebase's returned api call on a success
isSuccess: (values) => typeof values.apiDat.data !== 'undefined',
// this requires a definition based on firebase's returned api call when the call is unauthorized
isUnauthorized: (values) => typeof values.apiDat.detail !== 'undefined' && values.apiDat.detail === 'invalid_auth',
);
【讨论】:
感谢您的回复。这会很好,除非我不确定如何将 firebase 上下文直接引入状态机,因为我将状态机放在单独的文件中。我使用const firebase = useContext(FirebaseContext);
来获取firebase 上下文并访问.getTransactionState
函数。我想知道是否有另一种方法可以将 firebase 上下文直接传递到状态机?当我尝试在状态机文件中定义 firebase 时,我可以理解地得到“无法在顶层调用 React Hook “useContext”。”
您可以在状态机上创建一个“init”状态并在状态机上下文中设置一个值firebase
。接下来,当组件初始化时,您创建一个useEffect
,它将检查您在状态机中的firebase
值。如果未设置,则从您描述的 useContext
调用中更新它。现在状态机有了你的 firebase
值,转换到获取!
您能否澄清“当组件初始化时,您会创建一个 useEffect 来检查您在状态机中的 firebase 值”?我正在尝试实现您的代码,但我在我的useMachine
钩子中使用asEffect
,例如:actions: load: asEffect((context, event)=>context.firebase = firebase; context.transactionId = loadedState.id; )
。我现在收到“TypeError: Cannot read property 'getTransactionState' of null”,大概是因为我的状态机在我的钩子访问它之前正在运行。
那太好了。实际上,我根据我的代码和您的建议整理了一个小沙箱,以帮助说明我的意思。希望它有所帮助。随意更改您需要的任何内容。基本上在 App.js 第 30 行是我试图将 firebase 上下文发送到我的 stateMachine.js 文件的地方。 codesandbox.io/s/crazy-mendel-te0dl?file=/src/App.js
抱歉回复太晚了。我并没有真正让它发挥作用,现在,我最终做了@Jayce444 提到的事情。我从父组件中的 firebase 获取 machineConfig
并将结果作为道具传递给子组件(我在其中使用 useMachine
钩子)。在那里我检查machineConfig
prop 是否存在,如果存在,则通过setState
钩子将当前状态与useEffect
一起发送到父组件,该钩子也传递给子组件。如果machineConfig
不存在,则初始化为机器的初始状态。我也会尽快发布答案。以上是关于有没有办法使用从 Firebase 返回的承诺来初始化 Xstate 状态机?的主要内容,如果未能解决你的问题,请参考以下文章
Angular 2 Firebase Observable 承诺不会返回任何东西