XState와 결혼식을 올리는 Rsvp

나는 나의 결혼식 사이트를 위해 RSVP 표를 만들고 있는데, 나는 손님들이 그들의 거리 번호에 따라 자신을 찾을 수 있도록 하고 싶다.
즐거움의 길
결혼식장에서 행복의 길은 이렇다.
  • 가로번호 문의
  • 실행lookupGuest API 호출
  • 가로번호에 따라 손님을 찾았을 때 RSVP표
  • 표시
  • 손님이 RSVP표를 작성하여 제출
  • 엔드포인트submitRsvp에 게시
  • 감사 메시지 표시
  • 일은 매우 간단한 것 같다!나는 하룻밤에 그것을 완성할 수 있을 것이다.그런데 잠깐만...
    복잡성
  • 만약 우리가 거리 번호에 따라 손님을 찾지 못하면 어떻게 합니까?
  • 고객이 RSVP를 제출했다면 다음을 수행합니다.
  • 그들의 이전 반응을 보아야 한다.
  • 다시 제출할 수 없습니다.
  • 거리 번호는 유일한 것을 보장하지 않습니다. 왜냐하면 우리는 같은 주소로 여러 개의 초대장을 보냈기 때문입니다.
  • 이 API 호출 중 하나가 실패하면 어떻게 합니까?
  • 국가 기계 구조!


    본 훈련에서 나는 XState기계를 사용하여 이런 복잡한 문제를 해결할 것이다.
    그는 국가 기계를 전방 지역사회의 지도에 한 손으로 놓았다. (나는 그가 충분한 신뢰를 얻지 못했다고 생각한다.)내가 그의 내용을 소비할 때마다 나는 "와! 왜 모든 사람이 이렇게 하지 않는 거야?"라고 생각한다.
    그러나 실천 과정에서 나는 이미 그들을 여러 번 접촉한 적이 있다. 일은 항상 이렇다.
  • 나는 나의 사고방식을 어떻게 바꾸는지 기억하는 데 시간이 걸렸다(나는 나의 명령식 방식에 익숙해졌다).그리고 나는 문법을 찾는 데 약간의 시간이 필요하다.
  • 일단 내가 한다면 나는 그것을 좋아한다!그것은 매우 깨끗하고 유지하기 쉽다.
  • 그러나 그 후에 나는 그것들을 사용하지 않은 또 다른 프로젝트를 시작했고 모든 것을 잊어버렸다.
  • 국가기계와 엑스스테이트가 꼭 복잡한 괴물은 아니며 컴스키 박사가 논쟁해야 한다.만약 네가 가장 간단한 10퍼센트만 공부한다면, 너는 90퍼센트의 문제를 해결할 수 있을 것이다.
    내가 이 글을 쓰는 것은 나의 컨디션 습관을 공고히 하고 빠른 참고로 삼기 위해서이다.

    주의 정의


    우선 사용자 인터페이스가 있을 수 있는 모든 다른 상태를 고려해 보세요.RSVP 시나리오의 경우:
  • unknown - 여기서 길거리 번호
  • 로 찾아볼게요.
  • finding - 대기 /lookupGuestapi 호출
  • 할 때 마운트 표시기를 표시합니다
  • choosing - 여기서 입력한 거리 번호와 일치하는 손님 명단을 보여 드리겠습니다.
  • checkingRsvp - "순간적"상태입니다.라우터입니다.일단 한 명의 손님을 선택하면 그 손님이 이미 답장을 하고 보냈는지 즉시 검사한다responded 또는unresponded
  • unresponded - RSVP 테이블
  • 이 표시됩니다.
  • responded - 게스트 RSVPd의 읽기 전용 뷰를 표시합니다.마지막 단계final입니다.
  • 다음은 XState로 이 점을 표현하는 방법입니다.
    const rsvpMachine = Machine({
      id: 'rsvp',
      initial: 'unknown',
      context: { },
      states: {
        unknown: {},
        finding: {},
        choosing: {},
        checkingRsvp: {},
        unresponded: {},
        submitting: {},
        responded: {
          type: "final"
        },
      }
    });
    

    If you want to follow along, try pasting this state chart into the XState Visualizer. I built the entire thing this way first, then copy/pasted it into my project.


    컨텍스트 정의


    각 주 간에 어떤 데이터를 보존해야 합니까?
    내 예에서는guest 검색results과 Selected 검색guest이 될 것이다.나는 그것들을 모두 null로 설정하여 시작할 것이다.다음 단계에서 상태기는 상하문checkHasResponded 등 함수에 전달하여 어느 상태로 전환할지 결정한다.
    const checkHasResponded = (context) => context.guest && context.guest.rsvp;
    const checkHasNotResponded = (context) => context.guest && !context.guest.rsvp;
    const checkAlreadyChosen = (context) => context.guest;
    
    const rsvpMachine = Machine({
      id: 'rsvp',
      initial: 'unknown',
      context: {
        results: null,
        guest: null,
      },
      ...
    });
    

    사용자 제어 이벤트 정의


    각 상태에 대해 사용자가 수행할 수 있는 작업은 무엇입니까?
    예를 들어 FIND 상태에서는 unknown할 수 있지만 FIND 상태에서는 submitting할 수 없다.
  • unknown주에 있을 때 손님은 가로번호FIND에 따라 직접 작성하여 finding
  • 에 발송할 수 있다.
  • choosing 상태일 때 내빈은 CHOOSE 어떤 검색 결과가 그것들인지 선택하고 그것들을 checkingRsvp 상태로 보내야 한다.
  • 진입checkingRsvp은 자동으로 responded 또는 unresponded 상태로 전환해야 한다.
  • unresponded 상태일 때 고객은 SUBMIT 그들의 RSVP를 submitting 상태로 전환할 수 있다
  • 상태도에는 두 가지 뚜렷한 차이가 있다.
    너는 어떻게 finding부터 choosing까지 가니?너는 어떻게 submitting부터 responded까지 가니?
  • 이 두 가지는 모두 현시적인 사용자 상호작용이 아니라 API 호출과 관련이 있다.
  • 다음에 이 점을 소개하겠습니다.
  • 이것은 지금까지의 완전한 상태기다.상술한 이벤트는 on 속성을 사용하여 설정한 것이다.
    재미있는 것은checkingRsvp.여기에 이벤트 키가 비어 있습니다. 이것은 자동으로 터치된다는 것을 의미합니다.그 다음에 공백 이벤트 키를 여러 목표에게 전달하고, 목표마다 그에 상응하는 경로를 설정할 수 있는 조건이 있습니다.XState를 atransient transition라고 합니다.
    const checkHasResponded = (context) => context.guest && context.guest.rsvp;
    const checkHasNotResponded = (context) => context.guest && !context.guest.rsvp;
    const checkAlreadyChosen = (context) => context.guest;
    
    const rsvpMachine = Machine({
      id: "rsvp",
      initial: "unknown",
      context: {
        results: null,
        guest: null,
      },
      states: {
        unknown: {
          on: {
            FIND: "finding",
          },
        },
        finding: {},
        choosing: {
          on: {
            CHOOSE: "checkingRsvp",
          },
        },
        checkingRsvp: {
          on: {
            "": [
              {
                target: "unresponded",
                cond: checkHasNotResponded,
              },
              {
                target: "responded",
                cond: checkHasResponded,
              },
            ],
          },
        },
        unresponded: {
          on: {
            SUBMIT: "submitting",
          },
        },
        submitting: {},
        responded: {
          type: "final",
        },
      },
    });
    

    호출 서비스


    마지막 큰일은 finding 또는 submitting 상태에 들어갈 때 API 호출을 어떻게 하는지 알아내는 것이다.이것은 XState's invoke property를 통해 완성된 것이다.invoke 상태 설정finding에 대해 다음을 수행합니다.
  • 사용invoke.src 비동기 함수 호출lookupGuest
  • 설정onDone.target이 비동기 호출이 완료되었을 때 다음 상태로 전환
  • 설정onDone.actions-비동기식assign 결과event.data에서 context
  • XState 프로세스는 비동기 함수의 결과를 얻어 넣습니다event.data
  • const rsvpMachine = Machine({
      ...
      states: {
        ...
        finding: {
          invoke: {
            id: "lookupGuest",
            // Call the async fn
            src: (context, event) => lookupGuest(event.lookupId),
            onDone: {
              // once the async call is complete 
          // move to the 'choosing' state
              target: 'choosing',
              // use xstate's assign action to update the context
              actions: assign({ 
                // store the results in context
                results: (_, event) => event.data,
                // if there was only one result, set the guest
                guest: (_, event) => event.data.length === 1 ? event.data[0] : null
              })
            }
          },
        },
        ...
      },
    });
    
    submitting상태를 위해 같은 일을 실현한 후에 저는 RSVP상태기로 완성했습니다!

    UI에서 사용


    이러한 상태기를 사용하여 XState를 선택한 프레임(허브, React, Angular, Vue 등)과 함께 사용할 수 있습니다.
    다음은 React 사용법의 예입니다.state.value를 사용하여 현재 상태를 볼 수 있고 send를 사용하여 상태 전환 이벤트를 터치하여 상태기와 상호작용할 수 있습니다.
    function Rsvp() {
      const [state, send] = useMachine(rsvpMachine);
    
      if (state.value === "unknown") {
        return (
          <GuestLookupForm
            onSubmit={(streetNumber) =>
              send({ type: "FIND", lookupId: streetNumber })
            }
          />
        );
      }
    
      if (state.value === "finding") {
        return <Loading />;
      }
    
      if (state.value === "choosing") {
        return (
          <ChooseGuest
            guests={state.context.results}
            onSelect={(guest) => send({ type: "CHOOSE", guest})}
          />
        );
      }
    
      // ...You get the gist
    }
    

    결론


    나는 한두 시간 동안 상태도를 구축했지만 (모두 시각화 도구에 있음) 완성되면 사용자 인터페이스가 완전히 제자리에 앉는다.
    그래서 더 많은 일인 것 같지만, 이것은 매우 가치가 있다!어쨌든 너는 결국 이런 복잡한 문제들을 해결할 것이다.논리적 문제가 사용자 인터페이스의 괴벽에 의해 헷갈리기 전에 이런 문제들을 해결하면 해결 방안을 더욱 간결하고 유지하기 쉽다.
    이것 또한 자연스레 몇 가지 문제를 해결했다. 예를 들어 "내가 클릭할 때submit 단추를 사용하지 않는 것을 잊어버리고 사용자가 위에서 반복적으로 소란을 피우면 RSVP를 제출할 수 있습니까?"
    상태기를 사용할 때, 첫 번째 클릭은 submitting 로 변환되고, 사용자는 SUBMIT 동작을 보낼 수 있지만, submitting 상태는 무시됩니다.

    최종 결과


    다음은 상태도 최종 버전과 추가START_OVERonError 기능입니다.
    David statecharts.io Inspector에서 생성됨

    다음은 React에서 RSVP 상태기를 사용하는 코드sandbox 프레젠테이션입니다.최종 상태기 코드가 어떤 모양인지 알고 싶으면 원본 코드machine.js를 보세요.

    좋은 웹페이지 즐겨찾기