Skip to content

stradap/rematch

Repository files navigation

Rematch

Build Status Coverage Status Codacy Badge npm version file size file size

Rematch Logo

Rethink Redux.

Rematch is Redux best practices without the boilerplate. No more action types, action creators, switch statements or thunks. See a comparison.

Installation

npm install @rematch/core

Getting Started

Step 1: Init

init configures your reducers, devtools & store.

index.js

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.

Step 2: Models

The model brings together state, reducers, async actions & action creators in one place.

models.js

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:

  1. What is my initial state? state
  2. How do I change the state? reducers
  3. How do I handle async actions? effects with async/await

Step 3: Dispatch

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.

Examples

Complete Example

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')
)

API

See the API Reference.


Like this project? ★ us on Github :)

Packages

No packages published

Languages

  • JavaScript 70.8%
  • TypeScript 27.4%
  • Other 1.8%