[XState] context

이 글은 XState 공식문서를 읽고 정리한 글이다.

Context

XState에서 context는 상태 머신 actor에 데이터를 저장하는 방식이다.

context 프로퍼티는 모든 state에서 사용할 수 있고, actor와 관련된 데이터를 저장하는데 사용된다. context 객체는 immutable하고, 직접적으로 수정할 수 없다. 대신, 상태머신 로직의 경우 assign 액션을 사용하여 context를 업데이트할 수 있다.

context 프로퍼티는 옵셔널이다; 상태 머신이 finite states만을 명시하고 다른 외부 컨텍스트 데이터가 없다면, context는 필요없을 것이다.

import { createMachine } from "xstate";

const feedbackMachine = createMachine({
  // Initialize the state machine with context
  context: {
    feedback: "Some feedback",
  },
});

const feedbackActor = createActor(feedbackMachine);

feedbackActor.subscribe((state) => {
  console.log(state.context.feedback);
});

feedbackActor.start();
// logs 'Some feedback'

initial context

import { createMachine } from "xstate";

const feedbackMachine = createMachine({
  context: {
    feedback: "Some feedback",
    rating: 5,
    // other properties
  },
});

// lazy initial context
const feedbackMachine = createMachine({
  context: () => ({
    feedback: "Some feedback",
    createdAt: Date.now(),
  }),
});

const feedbackActor = createActor(feedbackMachine).start();

console.log(feedbackActor.getSnapshot().context.createdAt);
// logs the current timestamp

lazy 초기 context는 actor 단위로 평가되므로, 각 actor는 자체 context 객체를 갖게 된다.

input

input 데이터를 머신 초기 context로 주려면, createActor(machine, {input}) 함수로 input 프로퍼티를 전달하고, context 함수의 첫번째 인수에서 input 프로퍼티를 사용할 수 있다:

const feedbackMachine = createMachine({
  context: ({ input }) => ({
    feedback: "",
    rating: input.defaultRating,
  }),
});

const feedbackActor = createActor(feedbackMachine, {
  input: {
    defaultRating: 5,
  },
}).start();

console.log(feedbackActor.getSnapshot().context.rating);
// logs 5

assign(...)으로 context 수정하기

import { createMachine, assign } from "xstate";

const feedbackMachine = createMachine({
  context: {
    feedback: "Some feedback",
  },
  on: {
    "feedback.update": {
      actions: assign({
        feedback: ({ event }) => event.feedback,
      }),
    },
  },
});

const feedbackActor = createActor(feedbackMachine);

feedbackActor.subscribe((state) => {
  console.log(state.context.feedback);
});

feedbackActor.start();

// logs 'Some feedback'

feedbackActor.send({
  type: "feedback.update",
  feedback: "Some other feedback",
});

// logs 'Some other feedback'