reactjsxstate

Use external data in XState FSM


I'm trying to shim XState into an existing state management system (in a React app) and I'm trying to figure out how to represent the state that is already captured in the legacy state management without duplication.

import {useLegacyState} from 'legacy-state-system'
import {useMachine} from '@xstate/react'
import {MyMachine} from '../machine'

const MyComponent = () => {
  const [data, setData] = useLegacyState();
  const [state, send] = useMachine(MyMachine)

  .....JSX etc....
}

For some of the data there is no overlap, but in at least one case (selecting an item on screen, causes the app to send({type: "SELECT_ITEM", itemId: "xyz"}) and fire setData("XYZ")), both legacy and new systems care about the item. XState is being used for UI State Management but the legacy system has side effects that depends on its internal state, so I can't only have data in XState.

My understanding of XState is that I should represent itemId as continuous data in XState's context, but that duplicates the data and I'm concerned that presents a maintenance issue since all developers forever will need to know to update both simultaneously. Is there a way for XState Context to take a value from a runtime-evaluated function? I know that there's assign if I want to push values into Context but that's susceptible to the same maintenance issue so I'm looking for a way to pull values from legacy-state-manager when I call state.context.itemId.


Solution

  • What about wrapping useMachine and using that instead?

    import { useMachine as useXStateMachine } from '@xstate/react'
    
    export const useMachine = (machine, options) => {
      const [data, setData] = useLegacyState();
      const [state, send] = useXStateMachine(machine)
    
      const context = new Proxy({}, {
        get: (_, prop) => {
          try {
            return state.context[prop] || data[prop]
          } catch (_) {
            return data[prop]
          }
        }
      })
    
      return [{...state, context}, send]
    }