Skip to content

Instantly share code, notes, and snippets.

@fdaciuk
Created September 17, 2017 00:38
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fdaciuk/dd06e02a506318251cf1e7a37983d5d5 to your computer and use it in GitHub Desktop.
Save fdaciuk/dd06e02a506318251cf1e7a37983d5d5 to your computer and use it in GitHub Desktop.
Less boilerplate when create a new reducer on Redux

Create Reducer

Less boilerplate when create a new reducer on Redux

Method:

'use strict'

const createReducer = (initialState, actionHandlers) => {
  return (state = initialState, action) => {
    return actionHandlers.hasOwnProperty(action.type)
      ? actionHandlers[action.type](state, action)
      : state
  }
}

export default createReducer

Usage:

'use strict'

import { ADD_COUNTER, REMOVE_COUNTER, INCREMENT, DECREMENT } from './actions'
import createReducer from '../create-reducer'

export const initialState = []

export default createReducer(initialState, {
  [ADD_COUNTER]: (state, action) => state.concat(0),
  [REMOVE_COUNTER]: (state, action) => state.filter((_, i) => i !== action.index),
  [INCREMENT]: (state, action) => state.map((c, i) => i === action.index ? c + 1 : c),
  [DECREMENT]: (state, action) => state.map((c, i) => i === action.index ? c - 1 : c)
})
@fdaciuk
Copy link
Author

fdaciuk commented Sep 17, 2017

Remove duplication on INCREMENT and DECREMENT methods:

What is duplicated?

[INCREMENT]: (state, action) => state.map((c, i) => i === action.index ? c + 1 : c),
[DECREMENT]: (state, action) => state.map((c, i) => i === action.index ? c - 1 : c)

What is different on duplicated methods?

INCREMENT -> c + 1
DECREMENT -> c - 1

Let's use composition! First, create functions for increment and decrement, just with the different parts:

const inc = (c) => c + 1
const dec = (c) => c - 1

Nice! The only thing that was different is isolated! Now, let's create a method to isolate the duplication. This method will receive one of the above functions - to increment or decrement - by argument, and return a new function - the same that was passed to map methods above.
Before return this function, we need to return another function, to receive the selected index (action.index).
After that, instead of use c + 1 or c - 1, we can use the function inc or dec, passed by argument, and pass to this function the c value to it do what it must do:

const exec = (method) => (index) => (c, i) => i === index ? method(c) : c

Now, it's time to use exec on map functions to remove duplication:

[INCREMENT]: (state, action) => state.map(exec(inc)(action.index)),
[DECREMENT]: (state, action) => state.map(exec(dec)(action.index))

With this signature, we may create two another methods for increment or decrement on map:

const increment = exec(inc)
const decrement = exec(dec)

Then, pass those methods for map functions:

[INCREMENT]: (state, action) => state.map(increment(action.index)),
[DECREMENT]: (state, action) => state.map(decrement(action.index))

The final code:

'use strict'

import { ADD_COUNTER, REMOVE_COUNTER, INCREMENT, DECREMENT } from './actions'
import createReducer from '../create-reducer'

export const initialState = []

export default createReducer(initialState, {
  [ADD_COUNTER]: (state, action) => state.concat(0),
  [REMOVE_COUNTER]: (state, action) => state.filter((_, i) => i !== action.index),
  [INCREMENT]: (state, action) => state.map(increment(action.index)),
  [DECREMENT]: (state, action) => state.map(decrement(action.index))
})

const exec = (method) => (index) => (c, i) => i === index ? method(c) : c
const inc = (c) => c + 1
const dec = (c) => c - 1
const increment = exec(inc)
const decrement = exec(dec)

You can still improving this code, removing duplication of state.map. Just create a new function to execute the increment or decrement method (different parts):

const incDecReducer = (method) => (state, action) => state.map(method(action.index))

And use it:

[INCREMENT]: incDecReducer(increment),
[DECREMENT]: incDecReducer(decrement)

The final code:

'use strict'

import { ADD_COUNTER, REMOVE_COUNTER, INCREMENT, DECREMENT } from './actions'
import createReducer from '../create-reducer'

export const initialState = []

const incDecReducer = (method) => (state, action) => state.map(method(action.index))
const exec = (method) => (index) => (c, i) => i === index ? method(c) : c
const inc = (c) => c + 1
const dec = (c) => c - 1
const increment = exec(inc)
const decrement = exec(dec)

export default createReducer(initialState, {
  [ADD_COUNTER]: (state, action) => state.concat(0),
  [REMOVE_COUNTER]: (state, action) => state.filter((_, i) => i !== action.index),
  [INCREMENT]: incDecReducer(increment),
  [DECREMENT]: incDecReducer(decrement)
})

@suissa
Copy link

suissa commented Sep 17, 2017

não entendi o pq dos returns hhheeheheh

const createReducer = (initialState, actionHandlers) => (state = initialState, action) => 
    actionHandlers.hasOwnProperty(action.type)
      ? actionHandlers[action.type](state, action)
      : state
      
export default createReducer

@fdaciuk
Copy link
Author

fdaciuk commented Sep 17, 2017

@suissa só deixei pra facilitar a visualização =)

@suissa
Copy link

suissa commented Sep 17, 2017

Ahhhhhh eles tem q se acostumr a nao usar uhauhauhiaiuhuha

Mas ficou lindo man! Congratz!

@fdaciuk
Copy link
Author

fdaciuk commented Sep 17, 2017

Valeu :P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment