๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

FrontEnd

XState์˜ Context(์–‘์  ๋ฐ์ดํ„ฐ) ์•Œ์•„๋ณด๊ธฐ

๋ฐ˜์‘ํ˜•

https://xstate.js.org/docs/guides/context.html#initial-context

 

Context | XState Docs

Context ๐Ÿš€ Quick Reference While finite states are well-defined in finite state machines and statecharts, state that represents quantitative data (e.g., arbitrary strings, numbers, objects, etc.) that can be potentially infinite is represented as extende

xstate.js.org

 
์œ ํ•œ ์ƒํƒœ๋Š” ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„์™€ ์ƒํƒœ ์ฐจํŠธ์— ์ž˜ ์ •์˜๋˜์–ด ์žˆ์ง€๋งŒ,
์ž ์žฌ์ ์œผ๋กœ ๋ฌดํ•œํ•  ์ˆ˜ ์žˆ๋Š” ์–‘์ (quantitative) ๋ฐ์ดํ„ฐ(์˜ˆ: ์ž„์˜์˜ ๋ฌธ์ž์—ด, ์ˆซ์ž, ๊ฐœ์ฒด ๋“ฑ)๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ƒํƒœ๋Š”
ํ™•์žฅ ์ƒํƒœ(extended state)๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
 
์ด๊ฒƒ์€ ์ƒํƒœ ์ฐจํŠธ๋ฅผ ์‹ค์ œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ํ›จ์”ฌ ๋” ์œ ์šฉํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
XState์—์„  ํ™•์žฅ ์ƒํƒœ๋ฅผ ์ปจํ…์ŠคํŠธ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
๋ฌผ ํ•œ ์ปต์„ ์ฑ„์šฐ๋Š” ๊ฒƒ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ธฐ ์œ„ํ•ด ์ปจํ…์ŠคํŠธ๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ๋ฒ•์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค.
import { createMachine, assign } from 'xstate';

// Action to increment the context amount
const addWater = assign({
  amount: (context, event) => context.amount + 1
});

// Guard to check if the glass is full
function glassIsFull(context, event) {
  return context.amount >= 10;
}

const glassMachine = createMachine(
  {
    id: 'glass',
    // the initial context (extended state) of the statechart
    context: {
      amount: 0
    },
    initial: 'empty',
    states: {
      empty: {
        on: {
          FILL: {
            target: 'filling',
            actions: 'addWater'
          }
        }
      },
      filling: {
        // Transient transition
        always: {
          target: 'full',
          cond: 'glassIsFull'
        },
        on: {
          FILL: {
            target: 'filling',
            actions: 'addWater'
          }
        }
      },
      full: {}
    }
  },
  {
    actions: { addWater },
    guards: { glassIsFull }
  }
);
 
ํ˜„์žฌ ์ปจํ…์ŠคํŠธ๋Š” State์—์„œ state.context๋กœ ์ฐธ์กฐ๋ฉ๋‹ˆ๋‹ค.
const nextState = glassMachine.transition(glassMachine.initialState, {
  type: 'FILL'
});

nextState.context;
// => { amount: 1 }

Initial Context (์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ)


์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ๋Š” Machine์˜ ์ปจํ…์ŠคํŠธ ์†์„ฑ์— ์ง€์ •๋ฉ๋‹ˆ๋‹ค.
const counterMachine = createMachine({
  id: 'counter',
  // initial context
  context: {
    count: 0,
    message: 'Currently empty',
    user: {
      name: 'David'
    },
    allowedToIncrement: true
    // ... etc.
  },
  states: {
    // ...
  }
});

๋™์  ์ปจํ…์ŠคํŠธ(์ฆ‰, ์ดˆ๊ธฐ ๊ฐ’์ด ์™ธ๋ถ€์—์„œ ์ œ๊ณต๋˜๋Š” ์ปจํ…์ŠคํŠธ)์˜ ๊ฒฝ์šฐ ,

์ œ๊ณต๋œ ์ปจํ…์ŠคํŠธ ๊ฐ’์œผ๋กœ ๋จธ์‹ ์„ ์ƒ์„ฑํ•˜๋Š” ๋จธ์‹  ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๊ตฌํ˜„์€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ).

const createCounterMachine = (count, time) => {
  return createMachine({
    id: 'counter',
    // values provided from function arguments
    context: {
      count,
      time
    }
    // ...
  });
};

const counterMachine = createCounterMachine(42, Date.now());

 

๊ธฐ์กด ๋จธ์‹ ์˜ ๊ฒฝ์šฐ machine.withContext(...)๋ฅผ ์‚ฌ์šฉ ํ•ฉ๋‹ˆ๋‹ค.
const counterMachine = createMachine({
  /* ... */
});

// retrieved dynamically
const someContext = { count: 42, time: Date.now() };

const dynamicCounterMachine = counterMachine.withContext(someContext);
 
๋จธ์‹ ์˜ ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ๋Š” ์ดˆ๊ธฐ ์ƒํƒœ์—์„œ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
dynamicCounterMachine.initialState.context;
// => { count: 42, time: 1543687816981 }

์ด๊ฒƒ์€ ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ€ ์ดˆ๊ธฐ assign(...) ์ž‘์—…๊ณผ ์ผ์‹œ์ ์ธ ์ „ํ™˜(์žˆ๋Š” ๊ฒฝ์šฐ)์„ ํ†ตํ•ด ๊ณ„์‚ฐ๋˜๊ธฐ ๋•Œ๋ฌธ์—,

machine.context์— ์ง์ ‘ ์•ก์„ธ์Šคํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์„ ํ˜ธ๋ฉ๋‹ˆ๋‹ค. 

 

Assign Action (ํ• ๋‹น ์•ก์…˜)


assign() ์•ก์…˜์€ ์‹œ์Šคํ…œ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
ํ˜„์žฌ ์ปจํ…์ŠคํŠธ์˜ ๊ฐ’์„ ํ• ๋‹นํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‚˜ํƒ€๋‚ด๋Š”  ์ปจํ…์ŠคํŠธ "assigner"๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
Argument Type Description
assigner object or function The object assigner or function assigner which assigns values to the context (see below)
 
"assigner"๋Š” ๊ฐ์ฒด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๊ถŒ์žฅ).
import { createMachine, assign } from 'xstate';
// example: property assigner

// ...
  actions: assign({
    // increment the current count by the event value
    count: (context, event) => context.count + event.value,

    // assign static value to the message (no function needed)
    message: 'Count changed'
  }),
// ...
๋˜๋Š” ์—…๋ฐ์ดํŠธ๋œ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// example: context assigner

// ...

  // return a partial (or full) updated context
  actions: assign((context, event) => {
    return {
      count: context.count + event.value,
      message: 'Count changed'
    }
  }),
// ...
์œ„์˜ ์†์„ฑ ํ• ๋‹น์ž ๋ฐ ์ปจํ…์ŠคํŠธ ํ• ๋‹น์ž ํ•จ์ˆ˜ ์„œ๋ช…์—๋Š” ์ปจํ…์ŠคํŠธ, ์ด๋ฒคํŠธ ๋ฐ ๋ฉ”ํƒ€์˜ 3๊ฐ€์ง€ ์ธ์ˆ˜๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.
Argument Type Description
context TContext The current context (extended state) of the machine
event EventObject The event that triggered the assign action
meta 4.7+ AssignMeta an object with meta data (see below)
๋ฉ”ํƒ€ ๊ฐœ์ฒด์—๋Š” ๋‹ค์Œ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  • state - ์ผ๋ฐ˜ ์ „ํ™˜์˜ ํ˜„์žฌ ์ƒํƒœ(์ดˆ๊ธฐ ์ƒํƒœ ์ „ํ™˜์— ๋Œ€ํ•ด ์ •์˜๋˜์ง€ ์•Š์Œ)
  • action - assign action

๊ฒฝ๊ณ 

assign(...) ํ•จ์ˆ˜๋Š” ์ž‘์—… ์ƒ์„ฑ์ž์ž…๋‹ˆ๋‹ค. ์•ก์…˜ ๊ฐ์ฒด๋งŒ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ปจํ…์ŠคํŠธ์— ๋ช…๋ น์ ์œผ๋กœ ํ• ๋‹นํ•˜์ง€ ์•Š๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค

Action Order


๊ฒฝ๊ณ 
XState ๋ฒ„์ „ 5์—์„œ๋Š” assign(...) ์•ก์…˜์ด ์šฐ์„  ์ˆœ์œ„๊ฐ€ ์ง€์ •๋˜๋Š” ๋Œ€์‹  ์ˆœ์„œ๋Œ€๋กœ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
(์ด๋Š” SCXML์— ๋”ฐ๋ฅด๋ฉด ์ž˜๋ชป๋œ ๋™์ž‘์ž…๋‹ˆ๋‹ค.)
๋ฒ„์ „ 4์—์„œ ์ด๋Œ€๋กœ ๋™์ž‘ํ•˜๊ธธ ์›ํ•œ๋‹ค๋ฉด, ์‹œ์Šคํ…œ ๊ตฌ์„ฑ์— preserveActionOrder: true๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.
const counterMachine = createMachine({
  preserveActionOrder: true, // Ensures that assign actions are called in order
  // ...
  context: { count: 0 },
  states: {
    active: {
      on: {
        INC_TWICE: {
          actions: [
            (context) => console.log(`Before: ${context.count}`), // "Before: 0"
            assign({ count: (context) => context.count + 1 }), // count === 1
            assign({ count: (context) => context.count + 1 }), // count === 2
            (context) => console.log(`After: ${context.count}`) // "After: 2"
          ]
        }
      }
    }
  }
});

interpret(counterMachine).start().send({ type: 'INC_TWICE' });
// => "Before: 0"
// => "After: 2"
์‚ฌ์šฉ์ž ์ง€์ • ์•ก์…˜์€ ํ•ญ์ƒ ์ „ํ™˜์˜ ๋‹ค์Œ ์ƒํƒœ์™€ ๊ด€๋ จํ•˜์—ฌ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
์ƒํƒœ ์ „ํ™˜์— assign(...) ์ž‘์—…์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ์•ก์…˜์€ ํ•ญ์ƒ ์ผ๊ด„ ์ฒ˜๋ฆฌ๋˜๊ณ  ๋จผ์ € ๊ณ„์‚ฐ๋˜์–ด ๋‹ค์Œ ์ƒํƒœ๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
์ด๋Š” ์ƒํƒœ๊ฐ€ ์œ ํ•œ ์ƒํƒœ์™€ ํ™•์žฅ ์ƒํƒœ(์ปจํ…์ŠคํŠธ)์˜ ์กฐํ•ฉ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด ์ด ์นด์šดํ„ฐ ์‹œ์Šคํ…œ์—์„œ๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ์ž‘์—…์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
const counterMachine = createMachine({
  id: 'counter',
  context: { count: 0 },
  initial: 'active',
  states: {
    active: {
      on: {
        INC_TWICE: {
          actions: [
            (context) => console.log(`Before: ${context.count}`), // "Before: 2"
            assign({ count: (context) => context.count + 1 }), // count === 1
            assign({ count: (context) => context.count + 1 }), // count === 2
            (context) => console.log(`After: ${context.count}`) // "After: 2"
          ]
        }
      }
    }
  }
});

interpret(counterMachine).start().send({ type: 'INC_TWICE' });
// => "Before: 2"
// => "After: 2"

์ด๋Š” ๋‘ assign(...) ์•ก์…˜์ด ์ˆœ์„œ๋Œ€๋กœ ๋ฐฐ์น˜๋˜๊ณ , ๋จผ์ €(๋งˆ์ดํฌ๋กœ์Šคํ…์—์„œ) ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋‹ค์Œ ์ƒํƒœ ์ปจํ…์ŠคํŠธ๋Š” { count: 2 }์ด๋ฉฐ ๋‚˜๋จธ์ง€ ๋‘ ์‚ฌ์šฉ์ž ์ง€์ • ์ž‘์—…์— ๋ชจ๋‘ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

์ด ์ „ํ™˜์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ™œ์„ฑ ์ƒํƒœ์—์„œ INC_TWICE ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋‹ค์Œ ์ƒํƒœ๋Š” context.count๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ ํ™œ์„ฑ ์ƒํƒœ์ด๋ฉฐ,
์ด๋Ÿฌํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ์ž‘์—…์€ ํ•ด๋‹น ์ƒํƒœ์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ์ด๊ฒƒ์„ ๋ฆฌํŒฉํ† ๋งํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ช…์‹œ์ ์ธ ์ด์ „ ๊ฐ’์œผ๋กœ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ชจ๋ธ๋งํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
const counterMachine = createMachine({
  id: 'counter',
  context: { count: 0, prevCount: undefined },
  initial: 'active',
  states: {
    active: {
      on: {
        INC_TWICE: {
          actions: [
            (context) => console.log(`Before: ${context.prevCount}`),
            assign({
              count: (context) => context.count + 1,
              prevCount: (context) => context.count
            }), // count === 1, prevCount === 0
            assign({ count: (context) => context.count + 1 }), // count === 2
            (context) => console.log(`After: ${context.count}`)
          ]
        }
      }
    }
  }
});

interpret(counterMachine).start().send({ type: 'INC_TWICE' });
// => "Before: 0"
// => "After: 2"
์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • ํ™•์žฅ ์ƒํƒœ(์ปจํ…์ŠคํŠธ)๋Š” ๋ณด๋‹ค ๋ช…์‹œ์ ์œผ๋กœ ๋ชจ๋ธ๋ง๋ฉ๋‹ˆ๋‹ค.
  • ์•”์‹œ์  ์ค‘๊ฐ„ ์ƒํƒœ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์žก๊ธฐ ์–ด๋ ค์šด ๋ฒ„๊ทธ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์ž‘์—… ์ˆœ์„œ๋Š” ๋” ๋…๋ฆฝ์ ์ž…๋‹ˆ๋‹ค("์ด์ „" ๋กœ๊ทธ๋Š” "์ดํ›„" ๋กœ๊ทธ ๋’ค์— ์˜ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค!)
  • ์ƒํƒœ ํ…Œ์ŠคํŠธ ๋ฐ ๊ฒ€์‚ฌ ์ด‰์ง„ 

Notes (์ฃผ๋ชฉ)


  • ๐Ÿšซ ๊ธฐ๊ณ„์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์™ธ๋ถ€์—์„œ ๋ณ€๊ฒฝํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ๋ชจ๋“  ๊ฒƒ์€ ์ด์œ ๊ฐ€ ์žˆ๊ณ  ๋ชจ๋“  ์ปจํ…์ŠคํŠธ ๋ณ€๊ฒฝ์€ ์ด๋ฒคํŠธ๋กœ ์ธํ•ด ๋ช…์‹œ์ ์œผ๋กœ ๋ฐœ์ƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • assign({ ... })์˜ ๊ฐ์ฒด ๊ตฌ๋ฌธ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ–ฅํ›„ ๋ถ„์„ ๋„๊ตฌ์—์„œ ํŠน์ • ์†์„ฑ์ด ์„ ์–ธ์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋Š”์ง€ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ• ๋‹น์€ ๋ˆ„์ ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
// ...
  actions: [
    assign({ count: 3 }), // context.count === 3
    assign({ count: context => context.count * 2 }) // context.count === 6
  ],
// ...
  • ์•ก์…˜๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ assign() ์•ก์…˜์„ ๋ฌธ์ž์—ด์ด๋‚˜ ํ•จ์ˆ˜๋กœ ํ‘œํ˜„ํ•œ ๋‹ค์Œ ๋จธ์‹  ์˜ต์…˜์—์„œ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์Šต๋‹ˆ๋‹ค.
const countMachine = createMachine({
  initial: 'start',
  context: { count: 0 }
  states: {
    start: {
      entry: 'increment'
    }
  }
}, {
  actions: {
    increment: assign({ count: context => context.count + 1 }),
    decrement: assign({ count: context => context.count - 1 })
  }
});
  • ๋˜๋Š” ๋ช…๋ช…๋œ ํ•จ์ˆ˜๋กœ ๋ง์ž…๋‹ˆ๋‹ค. (์œ„์™€ ๋™์ผํ•œ ๊ฒฐ๊ณผ):
const increment = assign({ count: context => context.count + 1 });
const decrement = assign({ count: context => context.count - 1 });

const countMachine = createMachine({
  initial: 'start',
  context: { count: 0 }
  states: {
    start: {
      // Named function
      entry: increment
    }
  }
});
  • ์ด์ƒ์ ์œผ๋กœ ์ปจํ…์ŠคํŠธ๋Š” ์ผ๋ฐ˜ JavaScript ๊ฐ์ฒด๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, JSON์œผ๋กœ ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • assign() ์•ก์…˜์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์•ก์…˜์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ์ปจํ…์ŠคํŠธ๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.
    • ์ด๋Š” ๋™์ผํ•œ ๋‹จ๊ณ„ ๋‚ด์˜ ๋‹ค๋ฅธ ์•ก์…˜์ด assign() ์•ก์…˜์ด ์‹คํ–‰๋˜๊ธฐ ์ „์˜ ์ปจํ…์ŠคํŠธ๊ฐ€ ์•„๋‹Œ ์—…๋ฐ์ดํŠธ๋œ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ด์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹น์‹ ์˜ ์ƒํƒœ๋Š” ์•ก์…˜์˜ ์ˆœ์„œ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋˜์ง€๋งŒ, ์ด๋ฅผ ์—ผ๋‘์— ๋‘์‹ญ์‹œ์˜ค(์ถ”ํ›„ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ). ์ž์„ธํ•œ ๋‚ด์šฉ์€ action order๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ (์Šคํ‚ค๋งˆ)


์ ์ ˆํ•œ ํƒ€์ž… ์œ ์ถ”๋ฅผ ์œ„ํ•ด ์ปจํ…์ŠคํŠธ ํƒ€์ž…์„ ์‹œ์Šคํ…œ์˜ ์Šคํ‚ค๋งˆ ์†์„ฑ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

import { createMachine } from 'xstate';

interface CounterContext {
  count: number;
  user?: {
    name: string;
  };
}

const machine = createMachine({
  schema: {
    context: {} as CounterContext
  },
  // ...
  context: {
    count: 0,
    user: undefined
  }
  // ...
});

 

typeof ...๋ฅผ ์•ฝ์–ด๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
const context = {
  count: 0,
  user: { name: '' }
};

const machine = createMachine({
  schema: {
    context: {} as typeof context
  },
  // ...
  context
  // ...
});โ€‹

 

 
๊ทธ๋Ÿฌ๋‚˜ TypeScript ์ถ”๋ก ์€ ์™„๋ฒฝํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ปจํ…์ŠคํŠธ์™€ ์ด๋ฒคํŠธ๋ฅผ ์ œ๋„ค๋ฆญ์œผ๋กœ assign<Context, Event>(...)์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.
// ...
on: {
  INCREMENT: {
    // Generics guarantee proper inference
    actions: assign<CounterContext, CounterEvent>({
      count: (context) => {
        // context: { count: number }
        return context.count + 1;
      }
    });
  }
}
// ...
 

Quick Reference


์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ ์„ธํŒ…

const machine = createMachine({
  // ...
  context: {
    count: 0,
    user: undefined
    // ...
  }
});

 

๋™์  ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ ์„ธํŒ… (๋จธ์‹ ํŒฉํ„ฐ๋ฆฌ)

const createSomeMachine = (count, user) => {
  return createMachine({
    // ...
    // Provided from arguments; your implementation may vary
    context: {
      count,
      user
      // ...
    }
  });
};

 

๋™์  ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ ์„ธํŒ… (withContext)

const machine = createMachine({
  // ...
  // Provided from arguments; your implementation may vary
  context: {
    count: 0,
    user: undefined
    // ...
  }
});

const myMachine = machine.withContext({
  count: 10,
  user: {
    name: 'David'
  }
});

 

์ปจํ…์ŠคํŠธ์— ํ• ๋‹น

const machine = createMachine({
  // ...
  context: {
    count: 0,
    user: undefined
    // ...
  },
  // ...
  on: {
    INCREMENT: {
      actions: assign({
        count: (context, event) => context.count + 1
      })
    }
  }
});

 

ํ• ๋‹น (์ •์  ํ• ๋‹น - ์†์„ฑ ๋ณ„)

// ...
actions: assign({
  counter: 42
}),
// ...

 

ํ• ๋‹น (์†์„ฑ ๋ณ„ ํ• ๋‹น)

// ...
actions: assign({
  counter: (context, event) => {
    return context.count + event.value;
  }
}),
// ...

 

ํ• ๋‹น (์ปจํ…์ŠคํŠธ ํ†ต์งธ๋กœ)

 

// ...
actions: assign((context, event) => {
  return {
    counter: context.count + event.value,
    time: event.time,
    // ...
  }
}),
// ...

 

์—ฌ๋Ÿฌ๋ฒˆ ํ• ๋‹นํ•˜๊ธฐ

 

// ...
// assume context.count === 1
actions: [
  // assigns context.count to 1 + 1 = 2
  assign({ count: (context) => context.count + 1 }),
  // assigns context.count to 2 * 3 = 6
  assign({ count: (context) => context.count * 3 })
],
// ...
๋ฐ˜์‘ํ˜•