Skip to content

Commit

Permalink
Tweak what options are accepted by createActorContext and where (#3814
Browse files Browse the repository at this point in the history
)

* Tweak what options are accepted by `createActorContext` and where

* Refactor test

* Add changeset

* tweak a test title

---------

Co-authored-by: David Khourshid <davidkpiano@gmail.com>
  • Loading branch information
Andarist and davidkpiano committed Feb 10, 2023
1 parent d439fbe commit 494203b
Show file tree
Hide file tree
Showing 5 changed files with 412 additions and 47 deletions.
21 changes: 21 additions & 0 deletions .changeset/hip-bees-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@xstate/react': minor
---

The `Provider` from `createActorContext(...)` now accepts the `options={{...}}` prop that takes the same object as the second argument to the `useMachine(machine, options)` hook.

These options are no longer passed as the second argument to the `createActorContext(machine)` function:

```diff

-const SomeContext = createActorContext(someMachine,
- { actions: { ... } });
+const SomeContext = createActorContext(someMachine);

// ...

-<SomeContext.Provider>
+<SomeContext.Provider options={{ actions: { ... } }}>

// ...
```
60 changes: 51 additions & 9 deletions packages/xstate-react/src/createActorContext.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,80 @@
import * as React from 'react';
import { RestParams, useInterpret } from './useInterpret';
import { useInterpret } from './useInterpret';
import { useActor as useActorUnbound } from './useActor';
import { useSelector as useSelectorUnbound } from './useSelector';
import { ActorRefFrom, AnyStateMachine, EmittedFrom } from 'xstate';
import {
ActorRefFrom,
AnyStateMachine,
AreAllImplementationsAssumedToBeProvided,
EmittedFrom,
InternalMachineOptions,
InterpreterOptions,
Observer,
StateFrom
} from 'xstate';
import { UseMachineOptions } from './useMachine';

export function createActorContext<TMachine extends AnyStateMachine>(
machine: TMachine,
...[options = {}, observerOrListener]: RestParams<TMachine>
interpreterOptions?: InterpreterOptions,
observerOrListener?:
| Observer<StateFrom<TMachine>>
| ((value: StateFrom<TMachine>) => void)
): {
useActor: () => ReturnType<typeof useActorUnbound<ActorRefFrom<TMachine>>>;
useSelector: <T>(
selector: (snapshot: EmittedFrom<TMachine>) => T,
compare?: (a: T, b: T) => boolean
) => T;
useActorRef: () => ActorRefFrom<TMachine>;
Provider: (props: {
children: React.ReactNode;
machine?: TMachine | (() => TMachine);
}) => React.ReactElement<any, any>;
Provider: (
props: {
children: React.ReactNode;
machine?: TMachine | (() => TMachine);
} & (AreAllImplementationsAssumedToBeProvided<
TMachine['__TResolvedTypesMeta']
> extends false
? {
options: UseMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent']
> &
InternalMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent'],
TMachine['__TResolvedTypesMeta'],
true
>;
}
: {
options?: UseMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent']
> &
InternalMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent'],
TMachine['__TResolvedTypesMeta']
>;
})
) => React.ReactElement<any, any>;
} {
const ReactContext = React.createContext<ActorRefFrom<TMachine> | null>(null);

const OriginalProvider = ReactContext.Provider;

function Provider({
children,
machine: providedMachine = machine
machine: providedMachine = machine,
options
}: {
children: React.ReactNode;
machine: TMachine | (() => TMachine);
options?: any;
}) {
const actor = useInterpret(
providedMachine,
options,
{ ...interpreterOptions, ...options },
observerOrListener
) as ActorRefFrom<TMachine>;

Expand Down
59 changes: 29 additions & 30 deletions packages/xstate-react/src/useInterpret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,36 +85,35 @@ export function useIdleInterpreter(
return service as any;
}

export type RestParams<
TMachine extends AnyStateMachine
> = AreAllImplementationsAssumedToBeProvided<
TMachine['__TResolvedTypesMeta']
> extends false
? [
options: InterpreterOptions &
UseMachineOptions<TMachine['__TContext'], TMachine['__TEvent']> &
InternalMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent'],
TMachine['__TResolvedTypesMeta'],
true
>,
observerOrListener?:
| Observer<StateFrom<TMachine>>
| ((value: StateFrom<TMachine>) => void)
]
: [
options?: InterpreterOptions &
UseMachineOptions<TMachine['__TContext'], TMachine['__TEvent']> &
InternalMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent'],
TMachine['__TResolvedTypesMeta']
>,
observerOrListener?:
| Observer<StateFrom<TMachine>>
| ((value: StateFrom<TMachine>) => void)
];
type RestParams<TMachine extends AnyStateMachine> =
AreAllImplementationsAssumedToBeProvided<
TMachine['__TResolvedTypesMeta']
> extends false
? [
options: InterpreterOptions &
UseMachineOptions<TMachine['__TContext'], TMachine['__TEvent']> &
InternalMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent'],
TMachine['__TResolvedTypesMeta'],
true
>,
observerOrListener?:
| Observer<StateFrom<TMachine>>
| ((value: StateFrom<TMachine>) => void)
]
: [
options?: InterpreterOptions &
UseMachineOptions<TMachine['__TContext'], TMachine['__TEvent']> &
InternalMachineOptions<
TMachine['__TContext'],
TMachine['__TEvent'],
TMachine['__TResolvedTypesMeta']
>,
observerOrListener?:
| Observer<StateFrom<TMachine>>
| ((value: StateFrom<TMachine>) => void)
];

export function useInterpret<TMachine extends AnyStateMachine>(
getMachine: MaybeLazy<TMachine>,
Expand Down
17 changes: 10 additions & 7 deletions packages/xstate-react/test/createActorContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ describe('createActorContext', () => {
checkConsoleErrorOutputForMissingProvider();
});

it('should be able to pass interpreter options to the actor', () => {
it('should be able to pass interpreter options to the provider', () => {
const someMachine = createMachine({
initial: 'a',
states: {
Expand All @@ -314,19 +314,22 @@ describe('createActorContext', () => {
}
});
const stubFn = jest.fn();
const SomeContext = createActorContext(someMachine, {
actions: {
testAction: stubFn
}
});
const SomeContext = createActorContext(someMachine);

const Component = () => {
return null;
};

const App = () => {
return (
<SomeContext.Provider machine={someMachine}>
<SomeContext.Provider
machine={someMachine}
options={{
actions: {
testAction: stubFn
}
}}
>
<Component />
</SomeContext.Provider>
);
Expand Down

0 comments on commit 494203b

Please sign in to comment.