XSTATEを用いたPomodoroステートマシン


ステートマシンとXstateは間違いなく熱い🔥 最近話題になった多くの例を超えて、「ホームメイド」ポモドーロプロジェクトのための有限状態機械の作成を試みた.


Xstateについて少し
ドキュメント自体によると

XState is a library for creating, interpreting, and executing finite state machines and statecharts, as well as managing invocations of those machines as actors. The following fundamental computer science concepts are important to know how to make the best use of XState, and in general for all your current and future software projects.


このライブラリには豊富なよく維持されたドキュメントを持っている🤓
XState main page

ポモドーロ法🍅
これは時間管理のためのポピュラーな方法です.そして、あなたは持続的な集中を促進して、精神的な疲労を避けて、短くて長い中断でポモドーロ(集中した仕事セッション)を交替します.


Xstateの実装
この機械は4つの州を有する
  • FOCUS (初期状態)
  • SHORT_BREAK
  • LONG_BREAK
  • DONE (最終状態)
  • FOCUS , SHORT_BREAK and LONG_BREAK また、現在の状態がRUNNING or PAUSED . RUNNING は、最初の状態ですPAUSE and CONTINUE 遷移
    両方の州はFOCUS 一度COMPLETED が発射される.

    Link to visualizer
    コンテキストを使用して制御します.
  • フォーカス(分単位のフォーカス時間)
  • ショートブレーク(数分)
  • ロングブレイク
  • インターブレーク
  • 補完セクション
  • pausedtime (フォーカス時に設定された日付、短時間の区切りまたはlongount breakが一時停止)
  • timeout timeout (フォーカスのタイムアウト日、短時間の区切り、longount break )
  • しかし、あなたは自分自身に尋ねているかもしれません
    まあ、xstateを使用することができますActions , また、一般的に効果や副作用として知られています.これらのアクションでセクションを制御します.
    ここでのアプローチは、サービスを呼び出すことでしたtimerInterval すべてにRUNNING インナーステート.この間隔は、常にsectionTimeout and now .
    services: {
      timerInterval: ctx => cb => {
        const interval = setInterval(() => {
          const now = new Date()
          const timeDiff = ctx.sectionTimeout ?
            Math.ceil((ctx.sectionTimeout.valueOf() - now.valueOf()) / 1000) :
            undefined
    
          if (timeDiff !== undefined && timeDiff <= 0) {
            cb('COMPLETED')
          }
        }, 1000)
        return () => clearInterval(interval)
      }
    }
    
    以来sectionTimeout は機能の特定の日付ですnow + フォーカス時間、それは更新されているFOCUS entry 通しsetSectionTimeoutFromFocus . また、ユーザーが単にマシンを一時停止した場合に対処する必要があります.
    そうするために、我々は管理しているpausedTime with clearPausedTime and setPausedTime そして、それを更新する値を使用するsectionTimeout 適切に、ユーザーがマシンを停止するたびにsectionTimeout 更新する必要がありますnow + (sectionTimeout - pausedTime) .
    [TimerStates.FOCUS]: {
      entry: ['setSectionTimeoutFromFocus', 'clearPausedTime'],
      initial: SectionStates.RUNNING,
      states: {
        [SectionStates.RUNNING]: {
          entry: 'setSectionTimeoutFromDiffPausedTime',
          invoke: {
            id: 'timerInterval',
            src: 'timerInterval'
          },
          on: {
            PAUSE: SectionStates.PAUSED
          }
        },
        [SectionStates.PAUSED]: {
          entry: 'setPausedTime',
          on: {
            CONTINUE: SectionStates.RUNNING
          }
        }
      }
    }
    
    actions: {
      setSectionTimeoutFromFocus: assign({
        sectionTimeout: ctx => {
          const newSectionTimeout = new Date()
          newSectionTimeout.setMinutes(newSectionTimeout.getMinutes() + ctx.focus)
    
          return newSectionTimeout
        }
      }),
      setSectionTimeoutFromDiffPausedTime: assign({
        sectionTimeout: ctx => {
          if (!ctx.sectionTimeout || !ctx.pausedTime) {
            return ctx.sectionTimeout
          }
    
          const sectionTimeLeft = Math.ceil(
            ctx.sectionTimeout.valueOf() - ctx.pausedTime.valueOf()
          )
    
          const newSectionTimeout = new Date()
          newSectionTimeout.setMilliseconds(newSectionTimeout.getMilliseconds() + sectionTimeLeft)
    
          return newSectionTimeout
        }
      })
    }
    
    追加しましたsectionsCompleted and focusCompletedGoToLongBreak 次の状態遷移を決定するためにCOMPLETED がトリガされるtimerInterval サービス)completedSections また、FOCUS exit .
    exit: 'increaseCompletedSections',
    on: {
        COMPLETED: [
          {
            target: TimerStates.DONE,
            cond: 'sectionsCompleted'
          },
          {
            target: TimerStates.LONG_BREAK,
            cond: 'focusCompletedGoToLongBreak'
          },
          {
            target: TimerStates.SHORT_BREAK
          }
        ]
    }
    
    guards: {
      focusCompletedGoToLongBreak: ctx =>
        (ctx.completedSections + 1) % ctx.intervalsForLongBreak === 0,
      sectionsCompleted: ctx => ctx.sections === ctx.completedSections + 1
    }
    
    SHORT_BREAK and LONG_BREAK 州は同じ構造を有するFOCUS , を除いてCOMPLETED トランジションFOCUS .
    [TimerStates.SHORT_BREAK]: {
      entry: ['setSectionTimeoutFromShortBreak', 'clearPausedTime'],
      initial: SectionStates.RUNNING,
      states: {
        [SectionStates.RUNNING]: {
          entry: 'setSectionTimeoutFromDiffPausedTime',
          invoke: {
            id: 'timerInterval',
            src: 'timerInterval'
          },
          on: {
            PAUSE: SectionStates.PAUSED
          }
        },
        [SectionStates.PAUSED]: {
          entry: 'setPausedTime',
          on: {
            CONTINUE: SectionStates.RUNNING
          }
        }
      },
      on: {
        COMPLETED: TimerStates.FOCUS
      }
    },
    [TimerStates.LONG_BREAK]: {
      entry: ['setSectionTimeoutFromLongBreak', 'clearPausedTime'],
      initial: SectionStates.RUNNING,
      states: {
        [SectionStates.RUNNING]: {
          entry: 'setSectionTimeoutFromDiffPausedTime',
          invoke: {
            id: 'timerInterval',
            src: 'timerInterval'
          },
          on: {
            PAUSE: SectionStates.PAUSED
          }
        },
        [SectionStates.PAUSED]: {
          entry: 'setPausedTime',
          on: {
            CONTINUE: SectionStates.RUNNING
          }
        }
      },
      on: {
        COMPLETED: TimerStates.FOCUS
      }
    }
    

    ラッピング
    全体の実装はGithubで利用可能ですtimeTrackerMachine.ts )

    andrecrimb / pomodoro_rn
    あなたの時間を追跡し、Pomodoroメソッドを使用して生産性を向上させる.
    あなたがこの記事を楽しんだことを望みます💚
    また会いましょう👋🏽