Shawn McKay
App/Web Developer, Instructor
"INCREMENT_COUNT"
onChange callback
pre-hook
protected state
calculate state
send
events
Redux | Vuex | Mobx State Tree |
state | state | model |
getState | state | getSnapshot |
dispatch | dispatch | dispatch, commit |
action creators, reducers | mutations | actions |
subscribe | watch, subscribe | views |
devtools | devtools | devtools |
immutable | mutate | mutate |
equality check | reactive | reactive |
1 store* | 1 store* | multiple stores |
Vue only |
* generally
import { init } from '@rematch/core'
const store = init()
store.getState() // {}
import { init } from '@rematch/core'
import { Provider } from 'react-redux'
const store = init()
store.getState() // {}
const App = (
<Provider store={store}>
...
</Provider>
)
import { init } from '@rematch/core'
import * as models from './models'
const store = init({
models,
redux: {
initialState: {},
reducers: {},
middlewares: [],
enhancers: [],
rootReducers: {},
devtoolOptions: {},
// overwrites
combineReducers: fn,
createStore: fn,
}
})
// models.js
export const modelA = {
state: 'Hello, Blair!' // initial state
}
// models.js
export const modelA = {
state: 'Hello, Blair!' // initial state
}
export const modelB = {
state: 'Hello, Shawn!' // initial state
}
// models.js
export const modelA = {
state: 'Hello, Blair!' // initial state
}
export const modelB = {
state: 'Hello, Shawn!' // initial state
}
// index.js
import { init } from '@rematch/core'
import * as models from './models'
const store = init({
models
})
// models.js
export const modelA = {
state: 'Hello, Blair!' // initial state
}
export const modelB = {
state: 'Hello, Shawn!' // initial state
}
// index.js
import { init } from '@rematch/core'
import * as models from './models'
const store = init({
models
})
store.getState()
// {
// modelA: 'Hello, Blair!',
// modelB: 'Hello, Shawn!'
// }
export const count = {
state: 0,
reducers: {
up(state) {
return state + 1
},
upBy(state, payload) {
return state + payload
}
},
effects: {
async delayedUpBy(payload, rootState) {
await new Promise(r => setTimeout(r, 1000))
this.upBy(payload)
}
}
}
store.getState()
// { count: 0 }
No more action types
One reducer - One action
// Compared with a normal redux reducer...
const countReducer = (state = 0, action) => {
switch (action.type) {
case 'count/up': {
return state + 1
}
case 'count/upBy': {
return state + action.payload
}
default: return state
}
}
Rematch automatically gives you actions with dispatch built in
import { dispatch } from '@rematch/core'
// dispatch actions from anywhere!
dispatch.count.up()
dispatch.count.upBy(5)
Async actions (API calls)
Calls to dispatch other actions
Functions that talk to the "outside world"
Call them exactly like normal actions
import { dispatch } from '@rematch/core'
// dispatch actions from anywhere!
dispatch.count.up()
dispatch.count.upBy(5)
dispatch.count.delayedUpBy(5)
dispatch.count.upBy(1)
// ===
dispatch({ type: 'count/upBy', payload: 1 })
dispatch({ type: '' })
dispatch.model.action
import React from 'react'
import { connect } from 'react-redux'
import { dispatch } from '@rematch/core'
const Count = props => (
<div>
The count is {props.count}
<button onClick={() => dispatch.count.up()}>up</button>
<button onClick={() => dispatch.count.upBy(5)}>up by 5</button>
<button onClick={() => dispatch.count.delayedUpBy(5)}>delayed up by 5</button>
</div>
)
const mapState = state => ({
count: state.count
})
const CountContainer = connect(mapState)(Count)
dispatch({ type: '' })
dispatch.model.action
import React from 'react'
import { connect } from 'react-redux'
const Count = props => (
<div>
The count is {props.count}
<button onClick={props.up}>up</button>
<button onClick={props.upBy5}>up by 5</button>
<button onClick={props.delayedUpBy5}>delayed up by 5</button>
</div>
)
const mapState = state => ({
count: state.count
})
const mapDispatch = dispatch => ({
up: () => dispatch.count.up()
upBy5: () => dispatch.count.upBy(5),
delayedUpBy5: () => dispatch.count.delayedUpBy(5)
})
const CountContainer = connect(mapState, mapDispatch)(Count)
import { init } from '@rematch/core'
import * as models from './models'
import createLoadingPlugin from '@rematch/loading'
const loading = createLoadingPlugin()
const store = init({
models,
plugins: [loading]
})
import { init } from '@rematch/core'
import * as models from './models'
import createLoadingPlugin from '@rematch/loading'
const myCoolPlugin = {
config: { ... }, // merges into init config.
expose: { ... }, // A shared object for plugins to communicate with each other.
init: (expose) => {
onModel(model) { // called every time a model is created.
// do something
},
middleware: store => next => action => {
// do something
return next(action)
},
onStoreCreated(store) { // Run last, after the store is created.
// do something
}
}
}
const store = init({
models,
plugins: [myCoolPlugin]
})
const ThemeContext = React.createContext('light')
class ThemeProvider extends React.Component {
state = {theme: 'light'}
render() {
return (
<ThemeContext.Provider value={this.state.theme}>
{this.props.children}
</ThemeContext.Provider>
)
}
}
class App extends React.Component {
render() {
return (
<ThemeProvider>
<ThemeContext.Consumer>
{val => <div>{val}</div>}
</ThemeContext.Consumer>
</ThemeProvider>
)
}
}
Coming in 16.3
class Logger extends Plugin {
config = {
// init config
}
onModel(model) {
// run on each model
}
onStoreCreated() {
// run when store created
}
middleware: store => next => action => {
// add middleware
}
}
github.com/rematch
hiring!
semios.com/jobs
github.com/blairbodnar
github.com/ShMcK
By Shawn McKay