All Projects → sergeysova → Redux Symbiote

sergeysova / Redux Symbiote

Licence: mit
Create actions and reducer without pain

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Redux Symbiote

Mern Boilerplate
Fullstack boilerplate with React, Redux, Express, Mongoose, Passport Local, JWT, Facebook and Google OAuth out of the box.
Stars: ✭ 112 (-32.93%)
Mutual labels:  redux-thunk
Kea
Production Ready State Management for React
Stars: ✭ 1,805 (+980.84%)
Mutual labels:  redux-thunk
React Native Typescript Boilerplate
React Native Typescript starter kit / template (Redux Thunk + React Native Navigation v7 + TSLint)
Stars: ✭ 155 (-7.19%)
Mutual labels:  redux-thunk
React Cloud Music
React 16.8打造精美音乐WebApp
Stars: ✭ 1,722 (+931.14%)
Mutual labels:  redux-thunk
Typescript React Starter
🚀 TypeScript [ React + React-Router + Redux + Redux-Thunk ] Starter
Stars: ✭ 132 (-20.96%)
Mutual labels:  redux-thunk
React Redux Architecture
Learn how to architect a React Redux application in a classy way
Stars: ✭ 148 (-11.38%)
Mutual labels:  redux-thunk
React Native Redux Starter Kit
🚀 A starter boilerplate for a mobile app using React Native and Redux
Stars: ✭ 83 (-50.3%)
Mutual labels:  redux-thunk
Create React Redux App Structure
Create React + Redux app structure with build configurations ✨
Stars: ✭ 161 (-3.59%)
Mutual labels:  redux-thunk
Redux Most
Most.js based middleware for Redux. Handle async actions with monadic streams & reactive programming.
Stars: ✭ 137 (-17.96%)
Mutual labels:  redux-thunk
React Admin
基于[email protected]的react动态权限后台管理系统模板
Stars: ✭ 151 (-9.58%)
Mutual labels:  redux-thunk
Favesound Redux
🎶 A SoundCloud Client in React + Redux running in production. Live Demo and Source Code to explore React + Redux as a beginner.
Stars: ✭ 1,586 (+849.7%)
Mutual labels:  redux-thunk
Frontend Bootcamp
Frontend Workshop from HTML/CSS/JS to TypeScript/React/Redux
Stars: ✭ 10,506 (+6191.02%)
Mutual labels:  redux-thunk
Amazona
Build Ecommerce Like Amazon By MERN Stack
Stars: ✭ 152 (-8.98%)
Mutual labels:  redux-thunk
Koa React Universal
lightweight React-Koa2 universal boilerplate, only what is essential
Stars: ✭ 112 (-32.93%)
Mutual labels:  redux-thunk
Slopeninja Native
 Slope Ninja App 🎿❄️⛄️
Stars: ✭ 160 (-4.19%)
Mutual labels:  redux-thunk
Angular To React Redux
Angular to React/Redux, a Guide for Angular v1 Experts Looking to Learn React/Redux
Stars: ✭ 109 (-34.73%)
Mutual labels:  redux-thunk
React Admin
基于antd、redux-observable、redux-thunk、react-router响应式SPA脚手架,后台管理系统demo. 权限管理,用户管理,菜单管理。无限级菜单,下拉树形选择框
Stars: ✭ 141 (-15.57%)
Mutual labels:  redux-thunk
Firebase React Native Redux Starter
Starter For Firebase, React Native, Redux Applications With 100% Of Code In Common Between IOS And Android, with built In Authentication, Crud Example And Form Validation.
Stars: ✭ 166 (-0.6%)
Mutual labels:  redux-thunk
Space
A real time chat app for developers built using React, Redux, Electron and Firebase
Stars: ✭ 161 (-3.59%)
Mutual labels:  redux-thunk
React Interview Questions
300+ React Interview Questions
Stars: ✭ 151 (-9.58%)
Mutual labels:  redux-thunk

redux-symbiote Build Status Coverage Status All Contributors

Write your actions and reducers without pain

Usage

import { createSymbiote } from 'redux-symbiote'


const initialState = {
  error: null,
  accounts: [],
  loading: false,
}

const symbiotes = {
  accounts: {
    loading: {
      start: (state) => ({ ...state, loading: true }),
      failed: (state, error) => ({ ...state, loading: false, error }),
      finish: (state, accounts) => ({ ...state, loading: false, accounts }),
    },
  },
}

export const { actions, reducer } = createSymbiote(initialState, symbiotes)

Also you can use CommonJS:

const { createSymbiote } = require('redux-symbiote')

// ...

Demo

Edit Redux Symbiote Todos

API

Create symbiote

function createSymbiote(
  initialState,
  symbiotes,
  ?namespace = ''
)

Create action handlers + reducer

createSymbiote(initialState, {
  actionType: actionReducer,
  nestedType: {
    actionType: nestedActionReducer,
  }
})

Example:

const initialState = { value: 1, data: 'another' }

const symbiotes = {
  increment: (state) => ({ ...state, value: state.value + 1 }),
  decrement: (state) => ({ ...state, value: state.value - 1 }),
  setValue: (state, value) => ({ ...state, value }),
  setData: (state, data) => ({ ...state, data }),
  concatData: (state, data) => ({ ...state, data: data + state.data }),
}

export const { actions, reducer } = createSymbiote(initialState, symbiotes)

dispatch(actions.increment()) // { type: 'increment' }
dispatch(actions.setValue(4)) // { type: 'setValue', payload: [4] }
dispatch(actions.decrement()) // { type: 'decrement' }
dispatch(actions.setData('bar')) // { type: 'setData', payload: ['bar'] }
dispatch(actions.concatData('foo ')) // { type: 'concatData', payload: ['foo '] }

// State here { value: 3, data: 'foo bar' }

When you call actions.setValue symbiote calls your action handler with previousState and all arguments spreaded after state.

Nested example

const initialState = { value: 1, data: 'another' }

const symbiotes = {
  value: {
    increment: (state) => ({ ...state, value: state.value + 1 }),
    decrement: (state) => ({ ...state, value: state.value - 1 }),
  },
  data: {
    set: (state, data) => ({ ...state, data }),
    concat: (state, data) => ({ ...state, data: data + state.data }),
  },
}

export const { actions, reducer } = createSymbiote(initialState, symbiotes)

dispatch(actions.value.increment()) // { type: 'value/increment' }
dispatch(actions.value.decrement()) // { type: 'value/decrement' }
dispatch(actions.data.set('bar')) // { type: 'data/set', payload: ['bar'] }
dispatch(actions.data.concat('foo ')) // { type: 'data/concat', payload: ['foo '] }

Options

Third parameter in createSymbiote is optional string or object.

If string passed, symbiote converts it to { namespace: 'string' }.

Object has optional properties:

  • namespace is string — set prefix for each action type
  • defaultReducer is (previousState, action) -> newState — called instead of return previous state
  • separator is string — change separator of nested action types (default /)

ActionHandler##toString

You can use action as action type in classic reducer or in handleAction(s) in redux-actions

import { handleActions } from 'redux-actions'
import { createSymbiote } from 'redux-symbiote'

const initialState = { /* ... */ }

const symbiotes = {
  foo: {
    bar: {
      baz: (state, arg1, arg2) => ({ ...state, data: arg1, atad: arg2 }),
    },
  },
}

const { actions } = createSymbiote(initialState, symbiotes)

const reducer = handleActions({
  [actions.foo.bar.baz]: (state, { payload: [arg1, arg2] }) => ({
    ...state,
    data: arg1,
    atad: arg2,
  }),
}, initialState)

How to use reducer

createSymbiote returns object with actions and reducer.

Created reducer already handles created actions. You don't need to handle actions from symbiote.

// accounts.js
export const { actions, reducer } = createSymbiote(initialState, symbiotes, options)

// reducer.js
import { reducer as accounts } from '../accounts/symbiote'
// another imports

export const reducer = combineReducers({
  accounts,
  // another reducers
})

Why?

Redux recommends creating constants, action creators and reducers separately.

https://redux.js.org/basics/

const ACCOUNTS_LOADING_START = 'ACCOUNTS_LOADING_START'
const ACCOUNTS_LOADING_FAILED = 'ACCOUNTS_LOADING_FAILED'
const ACCOUNTS_LOADING_FINISH = 'ACCOUNTS_LOADING_FINISH'


export function loadingStart() {
  return {
    type: ACCOUNTS_LOADING_START,
  }
}

export function loadingFailed(error) {
  return {
    type: ACCOUNTS_LOADING_FAILED,
    payload: {
      error,
    },
  }
}

export function loadingFinish(accounts) {
  return {
    type: ACCOUNTS_LOADING_FINISH,
    payload: {
      accounts,
    },
  }
}

const initialState = {
  error: null,
  accounts: [],
  loading: false,
}

export function accountsReducer(state = initialState, action) {
  switch (action.type) {
    case ACCOUNTS_LOADING_START:
      return Object.assign({}, state, {
        loading: true,
      })

    case ACCOUNTS_LOADING_FAILED:
      return Object.assign({}, state, {
        loading: false,
        error: action.payload.error,
      })

    case ACCOUNTS_LOADING_FINISH:
      return Object.assign({}, state, {
        loading: false,
        accounts: action.payload.accounts,
      })
  }

  return state
}

So much boilerplate.

Let's look at redux-actions.

import { createActions, handleActions, combineActions } from 'redux-actions'


export const actions = createActions({
  accounts: {
    loading: {
      start: () => ({ loading: true }),
      failed: (error) => ({ loading: false, error }),
      finish: (accounts) => ({ loading: false, accounts }),
    },
  },
}).accounts

const initialState = {
  error: null,
  accounts: [],
  loading: false,
}

export const accountsReducer = handleActions({
  [combineActions(actions.loading.start, actions.loading.failed, actions.loading.finish)]:
    (state, { payload: { loading } }) => ({ ...state, loading }),

  [actions.loading.failed]: (state, { payload: { error } }) => ({ ...state, error }),

  [actions.loading.finish]: (state, { payload: { accounts } }) => ({ ...state, accounts }),
}, initialState)

But we have some duplicate in action creators properties and reducer.

Let's rewrite it to redux-symbiote:

import { createSymbiote } from 'redux-symbiote'

const initialState = {
  error: null,
  accounts: [],
  loading: false,
}

const symbiotes = {
  start: (state) => ({ ...state, loading: true }),
  finish: (state, { accounts }) => ({ ...state, loading: false, accounts }),
  failed: (state, { error }) => ({ ...state, loading: false, error }),
}

export const { actions, reducer: accountsReducer } =
  createSymbiote(initialState, symbiotes, 'accounts/loading')

That's all. accounts/loading is an optional namespace for actions types.

To reduce noise around loading actions try symbiote-fetching.

Contributors

Thanks goes to these wonderful people (emoji key):

Sergey Sova
Sergey Sova

📖 💻 💡 🤔 ⚠️
Arutyunyan Artyom
Arutyunyan Artyom

👀 🤔 🐛 💻
Igor Kamyshev
Igor Kamyshev

📦 ⚠️
Ilya
Ilya

🐛
Ivanov Vadim
Ivanov Vadim

📖
Аnton Krivokhizhin
Аnton Krivokhizhin

📦 🚇
Viacheslav
Viacheslav

🤔 👀
Dmitri Razin
Dmitri Razin

🐛 🎨
Surgie Finesse
Surgie Finesse

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].