xstate - 可重用的表单状态机
问题描述
在尝试围绕 xState 和一般状态机进行研究时,我想知道您将如何向表单状态机提供 API URL 以使其可重用。我目前的解决方案是通过withContext来提供,但是感觉不对。
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 行代码),但apiUrl
在提交表单时传递 as 数据send('SEND');
。所以而不是:
<form onSubmit={
(e) => {
e.preventDefault();
send('SEND');
}
}>
你可以试试:
<form onSubmit={
(e) => {
e.preventDefault();
send('SEND', { apiUrl: 'api/contact'});
}
}>
推荐阅读
- jenkins - Jenkins Build 总是失败,即使它应该是不稳定的
- python - 我应该如何使用 Pandas 中的类型列表列
- c - 我应该如何处理返回错误答案的函数?
- netbeans - Netbeans 是否支持 Windows 深色主题?
- javascript - 是否可以在定义之前在表达式中使用默认参数?
- surveyjs - 在surveyjs中处理文本更改时的可见性
- python - 覆盖范围和管道徽章未出现在 PyPI 上
- tinkerpop3 - 多线程事务中线程可以看到彼此的变化吗?
- ios - 快速在动态键和动态对象上使用 Codable
- css - yii2中的下拉列表在添加样式属性时失败