Skip to content
Permalink
Browse files

initial commit

  • Loading branch information...
carloslfu committed Nov 4, 2018
0 parents commit d96bf817f13ccb7d04b2dfbf3895c7d65323ce3f
Showing with 277 additions and 0 deletions.
  1. +6 −0 .editorconfig
  2. +2 −0 .gitignore
  3. +94 −0 README.md
  4. +85 −0 package-lock.json
  5. +36 −0 package.json
  6. +30 −0 src/index.ts
  7. +24 −0 tsconfig.json
@@ -0,0 +1,6 @@

[*.{js,jsx,ts,tsx}]

indent_style = space
indent_size = 2

@@ -0,0 +1,2 @@
node_modules/
lib/
@@ -0,0 +1,94 @@
# use-machine

Use Statecharts in React powered by XState, using the `useMachine` hook. This is a minimalistic implementation (just 30 lines) that integrates React and XState.

Install it with: `npm i use-machine`

Example use, [try it in codesandbox](https://codesandbox.io/s/5z0820jlyk):

```javascript
import React, { useContext } from 'react'
import ReactDOM from 'react-dom'
import { matchesState } from 'xstate'
import { assign } from 'xstate/lib/actions'
import { useMachine } from 'use-machine'
const incAction = assign(exState => ({ counter: exState.counter + 1 }))
const machineConfig = {
initial: 'Off',
context: {
counter: 0
},
states: {
Off: { on: { Tick: { target: 'On', actions: [incAction, 'sideEffect'] } } },
On: { on: { Tick: { target: 'Off', actions: incAction } } }
}
}
const MachineContext = React.createContext()
function App() {
const machine = useMachine(machineConfig, {
actions: {
sideEffect: () => console.log('sideEffect')
}
})
function sendTick() {
machine.send('Tick')
}
return (
<div className="App">
<span
style={{
backgroundColor: matchesState(machine.state, 'Off') ? 'red' : 'yellow'
}}
>
{matchesState(machine.state, 'Off') ? 'Off' : 'On'}
</span>
<button onClick={sendTick}>Tick</button>
Pressed: {machine.exState.counter} times
<MachineContext.Provider value={machine}>
<div className="childs">
<Child />
</div>
</MachineContext.Provider>
</div>
)
}
function Child() {
const machine = useContext(MachineContext)
return (
<div>
<div>
Child state: {matchesState(machine.state, 'Off') ? 'Off' : 'On'}
</div>
<div>Child count: {machine.exState.counter}</div>
<OtherChild />
</div>
)
}
function OtherChild() {
const machine = useContext(MachineContext)
function sendTick() {
machine.send('Tick')
}
return (
<div>
<div>
OtherChild state: {matchesState(machine.state, 'Off') ? 'Off' : 'On'}
</div>
<div>OtherChild count: {machine.exState.counter}</div>
<button onClick={sendTick}>Tick 2</button>
</div>
)
}
const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
```

Some generated files are not rendered by default. Learn more.

@@ -0,0 +1,36 @@
{
"name": "use-machine",
"version": "0.2.0",
"description": "Use Statecharts in React powered by XState",
"main": "lib/index.js",
"files": [
"lib/"
],
"types": "lib/index.d.ts",
"typings": "lib/index.d.ts",
"scripts": {
"test": "echo hmm...",
"build": "tsc"
},
"peerDependencies": {
"react": "16.7.0-alpha.0",
"react-dom": "16.7.0-alpha.0",
"xstate": "^4.0.1"
},
"devDependencies": {
"react": "16.7.0-alpha.0",
"react-dom": "16.7.0-alpha.0",
"typescript": "^3.0.3",
"xstate": "^4.0.1"
},
"keywords": [
"xstate",
"react",
"react-hook",
"statecharts",
"state-machines",
"javascript"
],
"author": "@carlsolfu",
"license": "MIT"
}
@@ -0,0 +1,30 @@
import { useState, useEffect } from 'react'
import { Machine, MachineConfig, EventObject, MachineOptions } from 'xstate'
import { interpret } from 'xstate/lib/interpreter'

export function useMachine<
ContextType = any,
StateType = any,
EventType extends EventObject = any
>(
config: MachineConfig<ContextType, StateType, EventType>,
options: MachineOptions<ContextType, EventType>,
initialContext: ContextType
) {
const machine = Machine(config, options, initialContext)
const [state, setState] = useState(machine.initialState)
const [exState, setExState] = useState(machine.context)
const [send, setSend] = useState()
useEffect(() => {
const interpreter = interpret(machine)
interpreter.init()
interpreter.onTransition(setState)
interpreter.onChange(setExState)
setSend(() => interpreter.send)
return () => {
interpreter.off(setState)
interpreter.off(setExState)
}
}, [])
return { state, send, exState }
}
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"pretty": true,
"rootDir": "src",
"sourceMap": false,
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noImplicitAny": false,
"noFallthroughCasesInSwitch": true,
"outDir": "lib",
"lib": ["es2018", "dom"]
},
"exclude": [
"node_modules",
"lib",
]
}

0 comments on commit d96bf81

Please sign in to comment.
You can’t perform that action at this time.