statelyai / xstate

Actor-based state management & orchestration for complex app logic.
https://stately.ai/docs
MIT License
26.98k stars 1.24k forks source link

Methods like `provide` are lost when implementing Higher Level Actor Logic #4868

Open sourcec0de opened 5 months ago

sourcec0de commented 5 months ago

Bug or feature request?

It would be great to have a more robust way of extending existing machines/actors.

Description:

Hello :wave: , something we noticed in the doc for higher-level-actor-logic

The spread operator is used to preserve the original actor logic. Doing this seems to drop the original prototype information so methods like provide go missing.

(Bug) Expected result:

When using a higher-order function to extend a machine, I would expect all types and original prototype methods to pass through.

i.e this should type correctly, and the function should be present.

withExtendedLogic(machine).provide({})

(Bug) Actual result:

provide is not a function

(Bug) Potential fix:

Instead of this

function withLogic(logic) {
  return { 
    ...logic, 
    transition() {}
  }
}

We ended up with this to preserve those methods. Also, shout out to @wfischer42 on our team at Discern. He's the one who implemented this fix.

export const cloneInstance = <T extends object>(input: T): T => {
  const prototypeDescriptors = Object.getOwnPropertyDescriptors(
    Object.getPrototypeOf(input),
  )
  const protoClone = Object.create(null, prototypeDescriptors)
  return Object.create(protoClone, Object.getOwnPropertyDescriptors(input)) as T
}

function withLogic(logic) {
  const logicClone = cloneInstance(logic)
  logicClone.transition = () => {}

  return logicClone
}
sourcec0de commented 5 months ago

@davidkpiano we ran across another issue that seems related. It appears that calling provide after extending with a higher-level machine function reverts to the previous machines' logic.

const machine = createMachine()
const machineWithLogging = withLogging(machine)
machineWithLogging.provide() // returns to old behavior without logging