Rematch is Redux best practices without the boilerplate. No more action types, action creators, switch statements or thunks. See a comparison.
npm install @rematch/core
init configures your reducers, devtools & store.
import { init } from '@rematch/core'
import * as models from './models'
const store = init({
models,
})
For a more advanced setup, see plugins and Redux config options.
The model brings together state, reducers, async actions & action creators in one place.
export const count = {
state: 0, // initial state
reducers: { // state changes with pure functions
increment: (state, payload) => state + payload,
decrement: (state, payload) => state - payload,
},
effects: { // state changes with impure functions
incrementIfOdd(payload, rootState) {
if (state.count % 2) {
this.increment(1)
}
},
// use async/await for async actions
async incrementAsync(payload, rootState) {
await delay(1000)
this.increment(payload)
}
}
}
Understanding models is as simple as answering a few questions:
- What is my initial state? state
- How do I change the state? reducers
- How do I handle async actions? effects with async/await
dispatch is how we trigger reducers & effects in your models. Dispatch standardizes your actions without the need for writing action types or action creators.
import { dispatch } from '@rematch/core'
// state = { count: 0 }
// reducers
dispatch({ type: 'count/increment', payload: 1 }) // state = { count: 1 }
dispatch.count.increment(1) // state = { count: 2 }
// effects
dispatch({ type: 'count/incrementAsync', payload: 1 }) // state = { count: 3 } after delay
dispatch.count.incrementAsync(1) // state = { count: 4 } after delay
Dispatch can be called directly, or with the dispatch[model][action](payload)
shorthand.
JS | React | Vue | Angular
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider, connect } from 'react-redux'
import { init } from '@rematch/core'
// State
const count = {
state: 0,
reducers: {
increment: (state, payload) => state + payload,
decrement: (state, payload) => state - payload,
},
effects: {
incrementIfOdd(payload, rootState) {
if (rootState.count % 2) {
this.increment(1)
}
},
async incrementAsync(payload, rootState) {
await delay(1000) // simulate async
this.increment(payload)
}
}
}
const models = {
count,
}
const store = init({
models,
})
// View
const Count = props => (
<div>
<h1>The count is: {props.count}</h1>
<button onClick={props.increment}>+1</button>
<button onClick={props.decrement}>-2</button>
<button onClick={props.incrementIfOdd}>Add 1 if Odd</button>
<button onClick={props.incrementAsync}>Add 2 Async</button>
</div>
)
const mapState = state => ({
count: state.count,
})
const mapDispatch = dispatch => ({
increment: () => dispatch.count.increment(1),
decrement: () => dispatch.count.decrement(2),
incrementIfOdd: () => dispatch.count.incrementIfOdd(),
incrementAsync: () => dispatch.count.incrementAsync(2),
})
const CountContainer = connect(mapState, mapDispatch)(Count)
ReactDOM.render(
<Provider store={store}>
<CountContainer />
</Provider>,
document.getElementById('root')
)
See the API Reference.
Like this project? ★ us on Github :)