可重用的表单状态机

  • 本文关键字:表单 状态机 xstate
  • 更新时间 :
  • 英文 :


在试图将我的头包裹在xState和状态机上时,我想知道如何向表单状态机提供API URL以使其可重用。我目前的解决方案是通过上下文提供它,但它感觉不对。

import { Machine, assign } from 'xstate';
const submitForm = async ({ formData, apiURL }) => {
const res = await fetch(apiURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
const message = await res.text();
return { status: res.status, message };
};
const formMachine = Machine({
id: 'form',
initial: 'idle',
context: {
formData: {},
apiURL: '',
},
states: {
idle: {
on: {
SEND: 'submitted',
INPUT: {
actions: assign({
formData: (ctx, { data }) => ({ ...ctx.formData, ...data }),
}),
},
},
},
submitted: {
id: 'form-submitted',
initial: 'pending',
states: {
pending: {
invoke: {
id: 'submitForm',
src: submitForm,
onDone: {
target: 'success',
actions: assign({
result: (ctx, event) => event.data,
}),
},
onError: {
target: 'failure',
actions: assign({
errorMessage: (ctx, event) => event.data,
}),
},
},
},
success: {},
failure: {
on: {
RETRY: 'pending',
SEND: 'pending',
},
},
},
},
},
});
export default formMachine;
import React from 'react';
import { useMachine } from '@xstate/react';
import formMachine from '../data/machines/form';
const ContactForm = () => {
const contactFormMachine = formMachine.withContext({
formData: {
name: '',
email: '',
message: '',
},
apiURL: '/api/contact',
});
const [current, send] = useMachine(contactFormMachine);
return (
<>
{
current.matches('submitted.success') ? (
<div>Message succesfully sent</div>
) : (
<form onSubmit={
(e) => {
e.preventDefault();
send('SEND');
}
}>
...
</form>
)
}
</>
);
};
export default ContactForm;

我认为您有一个很好的解决方案可以使该机器按原样重复使用。下面是来自 xstate 可视化工具存储库的示例,它可以让你对解决方案感觉更舒服:

const invokeSaveGist = (ctx: AppMachineContext, e: EventObject) => {
return fetch(`https://api.github.com/gists/` + ctx.gist!.id, {
method: 'post',
body: JSON.stringify({
description: 'Generated by XState Viz: https://xstate.js.org/viz',
public: true,
files: {
'machine.js': { content: e.code }
}
}),
headers: {
Authorization: `token ${ctx.token}`
}
}).then(async response => {
if (!response.ok) {
throw new Error((await response.json()).message);
}
return response.json();
});
};

如您所见,url 的"动态"部分也来自这里的机器上下文,当然,它是这里的 gist id,但它也可以是 url 的任何其他部分。

您可以考虑的另一种解决方案,尽管我无论如何都不认为它是"更好"的解决方案(可能少 3 行代码(,但在提交带有send('SEND');的表单时将apiUrl作为数据传递。所以代替:

<form onSubmit={
(e) => {
e.preventDefault();
send('SEND');
}
}>

您可以尝试:

<form onSubmit={
(e) => {
e.preventDefault();
send('SEND', { apiUrl: 'api/contact'});
}
}>

最新更新