-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[xstate/react] Add
createActorContext()
(#3778)
* Add createProvider * Fix types * Update packages/xstate-react/src/createProvider.tsx Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> * Tweak types within `createProvider` (#3781) * createProvider -> createActorContext * Allow `compare` to be passed to the `useSelector` (#3782) * Use `screen` API for accessing elements in the newly added test file * Update packages/xstate-react/src/createActorContext.tsx Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> * .useContext -> .useActorRef * Code juggle `createActorContext` a little bit (#3785) * Switch back useActorRef -> useContext, add value * Update packages/xstate-react/src/createActorContext.tsx Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> * Add tests for context hooks being used outside of the Provider (#3787) * Update packages/xstate-react/src/createActorContext.tsx Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> * Update packages/xstate-react/src/createActorContext.tsx Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> * Update packages/xstate-react/test/createActorContext.test.tsx Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> * Fix lint issues and rename useContext -> useActorRef * Remove redundant `any` * Fix hook names * fixed expectations in tests * Add changeset * Export createActorContext Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
- Loading branch information
1 parent
c7cd5aa
commit f12248b
Showing
6 changed files
with
424 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
--- | ||
'@xstate/react': minor | ||
--- | ||
|
||
The `createActorContext(...)` helper has been introduced to make global actors easier to use with React. It outputs a React Context object with the following properties: | ||
|
||
- `.Provider` - The React Context provider | ||
- `.useActor(...)` - A hook that can be used to get the current state and send events to the actor | ||
- `.useSelector(...)` - A hook that can be used to select some derived state from the actor's state | ||
- `.useActorRef()` - A hook that can be used to get a reference to the actor that can be passed to other components | ||
|
||
Usage: | ||
|
||
```jsx | ||
import { createActorContext } from '@xstate/react'; | ||
import { someMachine } from './someMachine'; | ||
|
||
// Create a React Context object that will interpret the machine | ||
const SomeContext = createActorContext(someMachine); | ||
|
||
function SomeComponent() { | ||
// Get the current state and `send` function | ||
const [state, send] = SomeContext.useActor(); | ||
|
||
// Or select some derived state | ||
const someValue = SomeContext.useSelector((state) => state.context.someValue); | ||
|
||
// Or get a reference to the actor | ||
const actorRef = SomeContext.useActorRef(); | ||
|
||
return (/* ... */); | ||
} | ||
|
||
function App() { | ||
return ( | ||
<SomeContext.Provider> | ||
<SomeComponent /> | ||
</SomeContext.Provider> | ||
); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import * as React from 'react'; | ||
import { useInterpret } from './useInterpret'; | ||
import { useActor as useActorUnbound } from './useActor'; | ||
import { useSelector as useSelectorUnbound } from './useSelector'; | ||
import { ActorRefFrom, AnyStateMachine, EmittedFrom } from 'xstate'; | ||
|
||
export function createActorContext<TMachine extends AnyStateMachine>( | ||
machine: TMachine | ||
): { | ||
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>; | ||
} { | ||
const ReactContext = React.createContext<ActorRefFrom<TMachine> | null>(null); | ||
|
||
const OriginalProvider = ReactContext.Provider; | ||
|
||
function Provider({ | ||
children, | ||
machine: providedMachine = machine | ||
}: { | ||
children: React.ReactNode; | ||
machine: TMachine | (() => TMachine); | ||
}) { | ||
const actor = useInterpret(providedMachine) as ActorRefFrom<TMachine>; | ||
|
||
return <OriginalProvider value={actor}>{children}</OriginalProvider>; | ||
} | ||
|
||
Provider.displayName = `ActorProvider(${machine.id})`; | ||
|
||
function useContext() { | ||
const actor = React.useContext(ReactContext); | ||
|
||
if (!actor) { | ||
throw new Error( | ||
`You used a hook from "${Provider.displayName}" but it's not inside a <${Provider.displayName}> component.` | ||
); | ||
} | ||
|
||
return actor; | ||
} | ||
|
||
function useActor() { | ||
const actor = useContext(); | ||
return useActorUnbound(actor); | ||
} | ||
|
||
function useSelector<T>( | ||
selector: (snapshot: EmittedFrom<TMachine>) => T, | ||
compare?: (a: T, b: T) => boolean | ||
): T { | ||
const actor = useContext(); | ||
return useSelectorUnbound(actor, selector, compare); | ||
} | ||
|
||
return { | ||
Provider, | ||
useActorRef: useContext, | ||
useActor, | ||
useSelector | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.