본문 바로가기

FrontEnd

XState 공식 문서 번역 : 서비스 호출(Invoking Services)

반응형

공식 문서 주소

 

Invoking Services | XState Docs

Invoking Services 🚀 Quick Reference Expressing the entire app's behavior in a single machine can quickly become complex and unwieldy. It is natural (and encouraged!) to use multiple machines that communicate with each other to express complex logic inst

xstate.js.org

XState

 
 

단일 시스템에서 전체 앱의 동작을 표현하는 것은 금방 복잡하고 어려워질 수 있습니다.

대신 복잡한 논리를 표현하기 위해 서로 통신하는 여러 기계를 사용하는 것은 자연스럽고 권장됩니다.

이것은 각 머신 인스턴스가 다른 "액터"(예: 프라미스 또는 기타 머신)와 이벤트(메시지)를 주고받고 이에 반응할 수 있는 "액터"로 간주되는 Actor 모델과 매우 유사합니다.

(즉, 시스템 안과 밖을 각각 Actor로 나누어 책임 분할)

 

머신이 서로 통신하기 위해 부모 머신은
자식 머신을 invokes하고
sendParent(...)를 통해 자식 머신에서 보낸 이벤트를 수신하거나,
자식 머신이 최종 상태에 도달할 때까지 기다립니다. 그 후 onDone 상태로 전환됩니다.
 
우리는 다음과 같은 서비스들을 상태 기계처럼 호출할 수 있습니다.
 
  • Promise : resolve 시 onDone 전환을 수행합니다. reject시 onError 전환을 수행합니다.
  • Callbacks : 부모로 이벤트를 송신하거나, 부모로부터 이벤트틀 수신할 수 있습니다.
  • Observables : 이벤트를 부모 머신에 보낼 수 있을 뿐만 아니라 완료될 때 신호를 보낼 수 있습니다. (받을 수는 없습니다.)
  • Machines : 이벤트를 보내고 받을 수도 있고, 최종 상태에 도달하면 부모 머신에 알릴 수도 있습니다. 

The invoke Property (invoke의 속성)


invocation은 다음을 포함하는 객체인 invoke 속성을 사용하여 상태 노드의 configuration에 정의됩니다.

  • src - 호출할 서비스의 소스, 다음과 같을 수 있습니다.
    • 상태 기계
    • Promise 리턴 함수
    • 콜백 핸들러를 리턴하는 함수
    • 옵저버블을 리턴하는 함수
    • 문자열 : 이 머신의 options.services에 정의되어 나열된 4가지 옵션 중 하나를 나타내는... 
    • { type: src }의 소스 문자열과 기타 메타데이터가 포함된 소스 개체
      • 대부분 다 이렇게 쓴다.

문자열 예제 : 이렇게 쓰고
나중에 밑에서 구현 가능함

  • id - 호출할 서비스의 유일한 식별자입니다.
  • onDone - (선택 사항) 다음과 같은 경우 수행할 전환:
    • 자식 머신이 최종 상태에 도달하거나
    • 호출된 약속이 reolved
    • 호출된 옵저버블이 완료
  • onError - (선택 사항) 호출된 서비스가 실행 오류를 만났을 때 취할 전환입니다.
  • autoForward - (선택 사항) 이 머신에 전송된 모든 이벤트가 호출된 자식에게도 전송(또는 전달)되어야 하는 경우 true(기본적으로 false) 
    • ⚠️ 모든 이벤트를 맹목적으로 전달하면 예기치 않은 동작 및/또는 무한 루프가 발생할 수 있으므로 autoForward를 true로 설정하지 마십시오.
    • 항상 명시적으로 이벤트를 보냅니다(send).또는, forwardTo(...) 액션 크리에이터를 사용하여 이벤트를 서비스에 직접 전달하는 것을 선호합니다.
  • data - (선택 사항, 상태 기계를 호출할 때만 사용됨)
    • 자식 머신 컨텍스트의 속성을 부모 머신 컨텍스트에 있는 해당 값을 반환하는 함수에 매핑하는 개체입니다.
    • 예를 들어 부모에 a가 있고 자식에 a가 있으면 덮어씀
    • 컨텍스트를 통째로 교환해버린다는것 같음. 확인 필요

주의사항

invoke.onDone과 혼동되는 상태에서 onDone 속성을 얻지 마십시오. 유사한 전환이지만 다른 것을 참조합니다.

  • 상태 노드의 onDone 속성은 최종 상태에 도달한(종료된) 복합 상태 노드를 나타냅니다.
  • invoke.onDone 속성은 수행 중인 호출(invoke.src)의 완료를 나타냅니다.
// ...
loading: {
  invoke: {
    src: someSrc,
    onDone: {/* ... */} // refers to `someSrc` being done
  },
  initial: 'loadFoo',
  states: {
    loadFoo: {/* ... */},
    loadBar: {/* ... */},
    loadingComplete: { type: 'final' }
  },
  onDone: 'loaded' // refers to 'loading.loadingComplete' being reached
}
// ...

Invoking Promises(프로미스 호출)


모든 Promise는 상태 머신으로 모델링될 수 있으므로 XState는 Promise를 그대로 호출할 수 있습니다.

Promise는 다음 중 하나를 수행할 수 있습니다.

  • resolve() :  onDone 전환을 수행합니다.
  • Reject()(또는 throw an erro) : onError 전환을 수행합니다.
호출된 Promise가 활성화된 상태가 Promise가 해결(settled)되기 전에 종료되면 Promise의 결과는 폐기됩니다.
// Function that returns a promise
// This promise might resolve with, e.g.,
// { name: 'David', location: 'Florida' }
const fetchUser = (userId) =>
  fetch(`url/to/user/${userId}`).then((response) => response.json());

const userMachine = createMachine({
  id: 'user',
  initial: 'idle',
  context: {
    userId: 42,
    user: undefined,
    error: undefined
  },
  states: {
    idle: {
      on: {
        FETCH: { target: 'loading' }
      }
    },
    loading: {
      invoke: {
        id: 'getUser', //invokeId
        src: (context, event) => fetchUser(context.userId), //  호출할 서비스의 소스,
        onDone: {
          target: 'success',
          actions: assign({ user: (context, event) => event.data })
        },
        onError: {
          target: 'failure',
          actions: assign({ error: (context, event) => event.data })
        }
      }
    },
    success: {},
    failure: {
      on: {
        RETRY: { target: 'loading' }
      }
    }
  }
});
resolved된 데이터는'done.invoke.<id>' 이벤트의  data 속성 아래에 위치합니다.
{
  type: 'done.invoke.getUser',
  data: {
    name: 'David',
    location: 'Florida'
  }
}

Promise Rejection(프로미스 거부)

Promise가 거부되면 { type: 'error.platform' } 이벤트와 함께 onError 전환이 수행됩니다.

오류 데이터는 이벤트의 data 속성에서 사용할 수 있습니다.

const search = (context, event) => new Promise((resolve, reject) => {
  if (!event.query.length) {
    return reject('No query specified');
    // or:
    // throw new Error('No query specified');
  }

  return resolve(getSearchResults(event.query));
});

// ...
const searchMachine = createMachine({
  id: 'search',
  initial: 'idle',
  context: {
    results: undefined,
    errorMessage: undefined,
  },
  states: {
    idle: {
      on: {
        SEARCH: { target: 'searching' }
      }
    },
    searching: {
      invoke: {
        id: 'search'
        src: search,
        onError: {
          target: 'failure',
          actions: assign({
            errorMessage: (context, event) => {
              // event is:
              // { type: 'error.platform', data: 'No query specified' }
              return event.data;
            }
          })
        },
        onDone: {
          target: 'success',
          actions: assign({ results: (_, event) => event.data })
        }
      }
    },
    success: {},
    failure: {}
  }
});

주의사항

onError 전환이 누락되고 Promise가 거부되면 상태 기계에 엄격 모드(strict mode)를 지정하지 않는 한 오류가 무시됩니다.

Strict 모드는 이 경우 기계를 중지하고 오류를 발생시킵니다. 

 

Invoking Callbacks (콜백 호출하기)


부모 머신으로 전송될 이벤트 스트림은 두 개의 인수를 취하는 함수인 콜백 핸들러를 통해 모델링할 수 있습니다.
  • callback - 전송된 이벤트와 함께 호출됩니다.
  • onReceive - 부모의 이벤트를 수신하는 리스너와 함께 호출됩니다. 
(선택 사항) 리턴 값은 현재 상태가 종료될 때 호출된 서비스에 대해 정리(예: 구독 취소, 메모리 누수 방지 등)를 수행하는 함수여야 합니다. 콜백은 Promise로 리턴 값을 자동으로 래핑하기 때문에 async/await 구문을 사용할 수 없습니다.
// ...
counting: {
  invoke: {
    id: 'incInterval',
    src: (context, event) => (callback, onReceive) => {
      // This will send the 'INC' event to the parent every second
      const id = setInterval(() => callback('INC'), 1000);

      // Perform cleanup
      return () => clearInterval(id);
    }
  },
  on: {
    INC: { actions: assign({ counter: context => context.counter + 1 }) }
  }
}
// ...​

Listening to Parent Events (부모 이벤트 리스닝하기)

호출된 콜백 핸들러에는 부모로부터 콜백 핸들러로 전송된 이벤트에 대한 리스너를 등록하는 두 번째 인수인 onReceive도 제공됩니다.
이를 통해 부모 머신과 호출된 콜백 서비스 간의 부모-자식 간 통신이 가능합니다.
예를 들어, 부모 머신은 자식 머신인 'ponger' 서비스에 'PING' 이벤트를 보냅니다.
자식 서비스는 onReceive(listener)를 사용하여 해당 이벤트를 수신 대기하고 응답으로 'PONG' 이벤트를 부모에게 다시 보낼 수 있습니다.
const pingPongMachine = createMachine({
  id: 'pinger',
  initial: 'active',
  states: {
    active: {
      invoke: {
        id: 'ponger',
        src: (context, event) => (callback, onReceive) => {
          // Whenever parent sends 'PING',
          // send parent 'PONG' event
          onReceive((e) => {
            if (e.type === 'PING') {
              callback('PONG');
            }
          });
        }
      },
      entry: send({ type: 'PING' }, { to: 'ponger' }),
      on: {
        PONG: { target: 'done' }
      }
    },
    done: {
      type: 'final'
    }
  }
});

interpret(pingPongMachine)
  .onDone(() => done())
  .start();
const counterInterval = (callback, receive) => {
  let count = 0;

  const intervalId = setInterval(() => {
    callback({ type: 'COUNT.UPDATE', count });
    count++;
  }, 1000);

  receive(event => {
    if (event.type === 'INC') {
      count++;
    }
  });

  return () => { clearInterval(intervalId); }
}

Invoking Observables (옵저버블 호출)


Observables(opens new window)는 시간이 지남에 따라 방출되는 값의 스트림입니다.
그것들을 한 번에 모두가 아니라 비동기적으로 값을 내보내는 배열/컬렉션으로 생각하십시오.
JavaScript에는 옵저버블이 많이 구현되어 있습니다. 가장 인기 있는 것은 RxJS입니다.
 
Observable을 상태 기계처럼 호출할 수 있습니다.
이벤트(문자열 또는 개체)를 부모 머신에 보낼 것으로 예상할 수 있지만, 부모의 이벤트는 수신하지 않습니다.
즉 단방향 통신입니다.
 
옵저버블 호출은 컨텍스트와 이벤트를 인수로 사용하고 관찰 가능한 이벤트 스트림(이벤트 객체)을 반환하는 함수입니다.
옵저버블은 호출된 상태가 종료되면 구독이 취소됩니다.
import { createMachine, interpret } from 'xstate';
import { interval } from 'rxjs';
import { map, take } from 'rxjs/operators';

const intervalMachine = createMachine({
  id: 'interval',
  initial: 'counting',
  context: { myInterval: 1000 },
  states: {
    counting: {
      invoke: {
        src: (context, event) =>
          interval(context.myInterval).pipe(
            map((value) => ({ type: 'COUNT', value })),
            take(5)
          ),
        onDone: 'finished'
      },
      on: {
        COUNT: { actions: 'notifyCount' },
        CANCEL: { target: 'finished' }
      }
    },
    finished: {
      type: 'final'
    }
  }
});
위의 intervalMachine은 옵저버블이 "완료"(값 방출 완료)될 때까지 이벤트 객체에 매핑된 interval(...)에서 이벤트를 수신합니다. "CANCEL" 이벤트가 발생하면 옵저버블이 삭제됩니다(.unsubscribe()는 내부적으로 호출됩니다).

TIP

Observable은 모든 호출에 대해 반드시 새로 생성될 필요는 없습니다.(cold)

대신 "hot observable"을 참조할 수 있습니다.

import { fromEvent } from 'rxjs';

const mouseMove$ = fromEvent(document.body, 'mousemove');

const mouseMachine = createMachine({
  id: 'mouse',
  // ...
  invoke: {
    src: (context, event) => mouseMove$
  },
  on: {
    mousemove: {
      /* ... */
    }
  }
});

Invoking Machines (상태 기계를 호출)


기계는 계층적으로 통신하며 호출된 기계는 다음과 같이 통신할 수 있습니다.
  • send(EVENT, { to: 'someChildId' }) 작업을 통한 부모-자식으로
  • sendParent(EVENT) 작업을 통해 자식에서 부모로.
호출된 상태 기계가 상태가 종료되면 머신이 중지됩니다.
import { createMachine, interpret, send, sendParent } from 'xstate';

// Invoked child machine
const minuteMachine = createMachine({
  id: 'timer',
  initial: 'active',
  states: {
    active: {
      after: {
        60000: { target: 'finished' }
      }
    },
    finished: { type: 'final' }
  }
});

const parentMachine = createMachine({
  id: 'parent',
  initial: 'pending',
  states: {
    pending: {
      invoke: {
        src: minuteMachine,
        // The onDone transition will be taken when the
        // minuteMachine has reached its top-level final state.
        onDone: 'timesUp'
      }
    },
    timesUp: {
      type: 'final'
    }
  }
});

const service = interpret(parentMachine)
  .onTransition((state) => console.log(state.value))
  .start();
// => 'pending'
// ... after 1 minute
// => 'timesUp'

 

Invoking with Context (context 정보와 함께 호출)

자식 머신은 데이터 속성이 있는 부모 머신의 컨텍스트에서 파생한 컨텍스트와 함께 호출할 수 있습니다. 예를 들어, 아래의 parentMachine은 초기 컨텍스트가 { duration: 3000 }인 새로운 timerMachine 서비스를 호출합니다.
const timerMachine = createMachine({
  id: 'timer',
  context: {
    duration: 1000 // default duration
  }
  /* ... */
});

const parentMachine = createMachine({
  id: 'parent',
  initial: 'active',
  context: {
    customDuration: 3000
  },
  states: {
    active: {
      invoke: {
        id: 'timer',
        src: timerMachine,
        // Deriving child context from parent context
        data: {
          duration: (context, event) => context.customDuration
        }
      }
    }
  }
});
assign(...)과 마찬가지로 자식 컨텍스트는 객체(선호) 또는 함수로 매핑될 수 있습니다.
// Object (per-property):
data: {
  duration: (context, event) => context.customDuration,
  foo: (context, event) => event.value,
  bar: 'static value'
}

// Function (aggregate), equivalent to above:
data: (context, event) => ({
  duration: context.customDuration,
  foo: event.value,
  bar: 'static value'
})

주의사항

데이터는 시스템에 정의된 기본 컨텍스트를 대체합니다.

기존 컨텍스트와 병합되지 않습니다. 이 동작은 다음 메이저 버전에서 변경됩니다.

Done Data (완료 시 부모에게 데이터 보내기)

자식 머신이 최상위 최종 상태에 도달하면 "done" 이벤트에서 부모에 데이터를 보낼 수 있습니다

(예: { type: 'done.invoke.someId', data: ... }).

이 "완료 데이터"는 최종 상태의 데이터 속성에 지정됩니다.

const secretMachine = createMachine({
  id: 'secret',
  initial: 'wait',
  context: {
    secret: '42'
  },
  states: {
    wait: {
      after: {
        1000: { target: 'reveal' }
      }
    },
    // 최종 상태의 데이터 프로퍼티. someId에 secret
    // (예: { type: 'done.invoke.someId', data: ... })
    reveal: {
      type: 'final',
      data: {
        secret: (context, event) => context.secret
      }
    }
  }
});

const parentMachine = createMachine({
  id: 'parent',
  initial: 'pending',
  context: {
    revealedSecret: undefined
  },
  states: {
    pending: {
      invoke: {
        id: 'secret',
        src: secretMachine,
        onDone: {
          target: 'success',
          actions: assign({
            revealedSecret: (context, event) => {
              // event is:
              // { type: 'done.invoke.secret', data: { secret: '42' } }
              return event.data.secret;
            }
          })
        }
      }
    },
    success: {
      type: 'final'
    }
  }
});

const service = interpret(parentMachine)
  .onTransition((state) => console.log(state.context))
  .start();
// => { revealedSecret: undefined }
// ...
// => { revealedSecret: '42' }

Sending Events (이벤트 전송하기)

  • 자식 머신에서 부모 머신으로 이벤트를 보내려면 sendParent(event)를 사용하십시오(send(...)와 동일한 인수를 사용).
  • 부모 시스템에서 자식 시스템으로 보내려면 send(event, { to: <child ID> })를 사용하십시오.

주의사항

send(...) 및 sendParent(...) 액션 생성자는 시스템에 이벤트를 명령적으로 보내지 않습니다.
무엇을 전송하는지 설명하는 액션 객체를 반환하는 순수 함수입니다(예: { type: 'xstate.send', event: ... }).
인터프리터는 이러한 개체를 읽은 다음 보냅니다.
다음은 서로 통신하는 pingMachine과 pongMachine이라는 두 기계의 예입니다.
import { createMachine, interpret, send, sendParent } from 'xstate';

// Parent machine
const pingMachine = createMachine({
  id: 'ping',
  initial: 'active',
  states: {
    active: {
      invoke: {
        id: 'pong',
        src: pongMachine
      },
      // Sends 'PING' event to child machine with ID 'pong'
      entry: send({ type: 'PING' }, { to: 'pong' }),
      on: {
        PONG: {
          actions: send({ type: 'PING' }, { to: 'pong', delay: 1000 })
        }
      }
    }
  }
});

// Invoked child machine
const pongMachine = createMachine({
  id: 'pong',
  initial: 'active',
  states: {
    active: {
      on: {
        PING: {
          // Sends 'PONG' event to parent machine
          actions: sendParent('PONG', {
            delay: 1000
          })
        }
      }
    }
  }
});

const service = interpret(pingMachine).start();

// => 'ping'
// ...
// => 'pong'
// ..
// => 'ping'
// ...
// => 'pong'
// ...

Sending Responses (응답 보내기)


호출된 서비스(또는 생성된(spawned) 액터)는 다른 서비스/액터에 응답할 수 있습니다.

즉, 다른 서비스/액터가 보낸 이벤트에 대한 응답으로 이벤트를 보낼 수 있습니다. 이것은 respond(...) 액션 생성자로 수행됩니다.

 

예를 들어, 아래의 '클라이언트' 시스템은 'CODE' 이벤트를 호출된 'auth-server' 서비스에 보내고

1초 후에 'TOKEN' 이벤트로 응답합니다.

 

import { createMachine, send, actions } from 'xstate';

const { respond } = actions;

const authServerMachine = createMachine({
  id: 'server',
  initial: 'waitingForCode',
  states: {
    waitingForCode: {
      on: {
        CODE: {
          actions: respond('TOKEN', { delay: 1000 })
        }
      }
    }
  }
});

const authClientMachine = createMachine({
  id: 'client',
  initial: 'idle',
  states: {
    idle: {
      on: {
        AUTH: { target: 'authorizing' }
      }
    },
    authorizing: {
      invoke: {
        id: 'auth-server',
        src: authServerMachine
      },
      entry: send({ type: 'CODE' }, { to: 'auth-server' }),
      on: {
        TOKEN: { target: 'authorized' }
      }
    },
    authorized: {
      type: 'final'
    }
  }
});

이 특정 예제는 동일한 효과를 위해 sendParent(...)를 사용할 수 있습니다.

차이점은 respond(...)가 이벤트를 수신한 이벤트의 원점으로 되돌려 보낼 것이라는 점입니다.

이 원점은 반드시 부모 머신이 아닐 수도 있습니다. 

 

Multiple Services (다중 서비스)


배열로 여러 서비스를 호출할 수 있습니다.
// ...
invoke: [
  { id: 'service1', src: 'someService' },
  { id: 'service2', src: 'someService' },
  { id: 'logService', src: 'logService' }
],
// ...
 
각 호출은 해당 서비스의 새 인스턴스를 생성하므로 여러 서비스의 src가 동일하더라도(예: 위의 'someService') 'someService'의 여러 인스턴스가 호출됩니다. 



Configuring Services (서비스 설정하기)


호출 소스(서비스)는 src를 문자열로 지정하고

Machine 옵션의 services 속성에 정의하여 액션, 가드 등이 구성되는 방식과 유사하게 구성할 수 있습니다.

const fetchUser = // (same as the above example)

const userMachine = createMachine(
  {
    id: 'user',
    // ...
    states: {
      // ...
      loading: {
        invoke: {
          src: 'getUser',
          // ...
        }
      },
      // ...
    }
  },
  {
  services: {
    getUser: (context, event) => fetchUser(context.user.id)
  }
);

invoke src는 타입 및 기타 관련 메타데이터로 호출 소스를 설명하는 객체로도 지정할 수 있습니다.

meta.src 인수의 services 옵션에서 읽을 수 있습니다.

const machine = createMachine(
  {
    initial: 'searching',
    states: {
      searching: {
        invoke: {
          src: {
            type: 'search',
            endpoint: 'example.com'
          }
          // ...
        }
        // ...
      }
    }
  },
  {
    services: {
      search: (context, event, { src }) => {
        console.log(src);
        // => { endpoint: 'example.com' }
      }
    }
  }
);

Testing


위처럼, 문자열로 서비스를 지정하면 .withConfig()를 사용하여 대체 구현을 지정하여 "모의" 서비스를 수행할 수 있습니다.
import { interpret } from 'xstate';
import { assert } from 'chai';
import { userMachine } from '../path/to/userMachine';

const mockFetchUser = async (userId) => {
  // Mock however you want, but ensure that the same
  // behavior and response format is used
  return { name: 'Test', location: 'Anywhere' };
};

const testUserMachine = userMachine.withConfig({
  services: {
    getUser: (context, event) => mockFetchUser(context.id)
  }
});

describe('userMachine', () => {
  it('should go to the "success" state when a user is found', (done) => {
    interpret(testUserMachine)
      .onTransition((state) => {
        if (state.matches('success')) {
          assert.deepEqual(state.context.user, {
            name: 'Test',
            location: 'Anywhere'
          });

          done();
        }
      })
      .start();
  });
});

Referencing Services 4.7+


서비스(및 스폰된 서비스인 액터)는 .children 속성의 상태 개체에서 직접 참조할 수 있습니다.

state.children 객체는 서비스 ID(키)를 해당 서비스 인스턴스(값)에 매핑합니다.

const machine = createMachine({
  // ...
  invoke: [
    { id: 'notifier', src: createNotifier },
    { id: 'logger', src: createLogger }
  ]
  // ...
});

const service = interpret(machine)
  .onTransition((state) => {
    state.children.notifier; // service from createNotifier()
    state.children.logger; // service from createLogger()
  })
  .start();
JSON이 직렬화되면 state.children 객체는 해당 서비스에 대한 메타 데이터를 포함하는 객체에 대한 서비스 ID(키) 매핑입니다.

Quick Reference


The invoke property

const machine = createMachine({
  // ...
  states: {
    someState: {
      invoke: {
        // The `src` property can be:
        // - a string
        // - a machine
        // - a function that returns...
        src: (context, event) => {
          // - a promise
          // - a callback handler
          // - an observable
        },
        id: 'some-id',
        // (optional) forward machine events to invoked service (currently for machines only!)
        autoForward: true,
        // (optional) the transition when the invoked promise/observable/machine is done
        onDone: { target: /* ... */ },
        // (optional) the transition when an error from the invoked service occurs
        onError: { target: /* ... */ }
      }
    }
  }
});

Invoking Promises

// Function that returns a promise
const getDataFromAPI = () => fetch(/* ... */)
    .then(data => data.json());


// ...
{
  invoke: (context, event) => getDataFromAPI,
  // resolved promise
  onDone: {
    target: 'success',
    // resolved promise data is on event.data property
    actions: (context, event) => console.log(event.data)
  },
  // rejected promise
  onError: {
    target: 'failure',
    // rejected promise data is on event.data property
    actions: (context, event) => console.log(event.data)
  }
}
// ...

Invoking Callbacks

// ...
{
  invoke: (context, event) => (callback, onReceive) => {
    // Send event back to parent
    callback({ type: 'SOME_EVENT' });

    // Receive events from parent
    onReceive(event => {
      if (event.type === 'DO_SOMETHING') {
        // ...
      }
    });
  },
  // Error from callback
  onError: {
    target: 'failure',
    // Error data is on event.data property
    actions: (context, event) => console.log(event.data)
  }
},
on: {
  SOME_EVENT: { /* ... */ }
}

Invoking Observables

import { map } from 'rxjs/operators';

// ...
{
  invoke: {
    src: (context, event) => createSomeObservable(/* ... */).pipe(
        map(value => ({ type: 'SOME_EVENT', value }))
      ),
    onDone: 'finished'
  }
},
on: {
  SOME_EVENT: /* ... */
}
// ...

Invoking Machines

const someMachine = createMachine({ /* ... */ });

// ...
{
  invoke: {
    src: someMachine,
    onDone: {
      target: 'finished',
      actions: (context, event) => {
        // Child machine's done data (.data property of its final state)
        console.log(event.data);
      }
    }
  }
}
// ...





 



반응형