可重用状态机

时间:2020-01-08 10:43:11

标签: 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;

1 个答案:

答案 0 :(得分:0)

我认为您有一个很好的解决方案,可以使该计算机按原样重用。这是xstate可视化工具存储库中的an example,可以使您对解决方案更满意:

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的“动态”部分也是从机器上下文派生的,当然,这是此处的要点ID,但也可以是URL的其他任何部分。

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

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

您可以尝试:

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