Description
When using a variable from the context that has the type Actor, the useService hook (from the @xstate/react package) throws a type error:
Argument of type 'Actor<...>' is not assignable to parameter of type 'Interpreter<...>'.
When using the useActor hook (from the @xstate/react package) the values it returns stay undefined.
In 4.6 I could use the code bellow as fix (from issue #653 ):
const [refState, sendToRef] = useService(machine.context.ref! as unknown as Interpreter<RefContext, RefSchema, RefEvent>)
But that does not work anymore in xstate 4.7.
Expected Result
I could use useActor with a var actor that returns current and send.
Actual Result
useActor returns undefined
Reproduction
import * as React from "react";
import { useActor } from "@xstate/react";
import { ProductOptionStepType } from "../../../Types/Types";
interface IFormatProps {
option: ProductOptionStepType;
}
const Format: React.FC<IFormatProps> = ({ option }) => {
const [current, send] = useActor(option.ref);
}
This reported TS error was the symptom that lead to my questions in #672:
Finally, most of these concepts trickle out as confusing when you start to use the useService and useActor hooks. It seems that Actors are more locked down in state access, but it鈥檚 unclear as to why? It鈥檚 much easier to just use a useService, but as already noted, most of the Actors coming out of current.children and spawn aren鈥檛 accepted in useService. It鈥檚 unclear when comparing services to actors on why you鈥檇 ever want an Actor vs an Interpreter, or vice versa.
Some good discussion there about the concepts, but in practice it'd still be super helpful for all actors from invoke or spawn to work with useService
@derek-duncan Yeah! But I'm wondering, what now? What can I do in 4.7 do make it work?
@DirkWolthuis Looks like useService works with spawn in 4.7.0, and it's just TS that doesn't like it. Take a look at the Todo example upgraded to 4.7 https://codesandbox.io/s/xstate-todomvc-oj9ve. But I'm not sure how the Todo example could be converted to useActor to make TS happy. useActor only returns const [latestEvent, send] = useActor(spawnRef) which doesn't expose the interpreter state to access .matches and .context.
So for now, keep using useService and just cast the ref to as any.
NOTE: _Only_ actors created from spawn _in a machine_ (aka spawn refs assigned in the context) can be used in useService, since they are really an Interpreter behind the scenes (see #664), which is the only thing useService accepts. If you try to use state.children.actorRef to access your actor refs instead of state.context.actorRef, you will get an Actor object which will not work inside useService.
@derek-duncan Thanks! This works for me.
Two questions though:
What is the difference between state / current?
There is no difference.
For the second issue, I'll investigate this.
Hi @davidkpiano any update. Something I can do?.
It would be safe (as a workaround, for now) to check that state exists.
@davidkpiano not sure if it helps, but I think state/current is undefined initially because, for some reason, assign actions that are calling spawn are getting called twice in a row (at least for onEntry).
The first time the action is called, the spawn inside outputs an Actor, but the second time the action is called, the spawn outputs an Interpreter. The first call also strangely throws a warning about being called outside a service, even though it's not.
If useService receives an Actor (from the first assign call), it doesn't have access to the current state. However, when it receives the Interpreter (from the second assign call), it works correctly.

Ah, the above issue is fixed by #758 so that the warning is gone and the initial call produces an Interpreter. The entry action is still called twice but that doesn't seem to be an issue.
Is there a way for me to test with master (installing via NPM?)
@DirkWolthuis not currently, but it will be in the future (thanks for Codesandbox CI). Right now you鈥檇 have to download the repository, build it, use npm pack and install created tarball (or use npm link)
That sounds scary! I will wait until it's merged into 4.7 (if that's the plan)
This should now work in 4.7
@davidkpiano Given the information in this issue as well as #424 and #664 I updated an example to the latest version (4.7.6) but I can still not get it to work. I expect the child state label to update as the console does.
Is there anyone that can explain what I'm doing wrong?
Most helpful comment
There is no difference.
For the second issue, I'll investigate this.