[Functional Programming] Rewrite a reducer with functional state ADT

For example we have a feature reducer like this:

// selectCard :: String -> Action String
export const selectCard =
  createAction(SELECT_CARD)

// showFeedback :: String -> Action String
export const showFeedback =
  createAction(SHOW_FEEDBACK)

// reducer :: Action a -> (State AppState ()) | Null
const reducer = ({ type, payload }) => {
  switch (type) {
    case SELECT_CARD:
      return answer(payload)

    case SHOW_FEEDBACK:
      return feedback(payload)
  }

  return null
}

export default reducer

First of all, we can replace 'swtich' statement with normal Object:

const actionReducer = {
  SELECT_CARD: answer,
  SHOW_FEEDBACK: feedback
}  

// reducer :: Action a -> (State AppState ()) | Null
const reducer = ({ type, payload }) => (actionReducer[type] || Function.prototype)(payload)

In case of ´actionReducer[type]´ return undefined, we default a function by `Function.prototype`, ready to take a payload.

const reducer = ({ type, payload }) => (actionReducer[type] || Function.prototype)(payload)

This type of code is suitable for using 'Maybe'.

// createReducer :: ActionReducer -> Reducer
export const createReducer = actionReducer =>
  ({ type, payload }) =>
    prop(type, actionReducer)
      .map(applyTo(payload))

Refactor:

// showFeedback :: String -> Action String 
export const showFeedback =
  createAction(SHOW_FEEDBACK)

const reducer = createReducer({
  SELECT_CARD: answer,
  SHOW_FEEDBACK: feedback
})

// reducer :: Action a -> (State AppState ()) | Null
// const reducer = ({ type, payload }) => 
//   (actionReducer[type] || Function.prototype)(payload)

export default reducer

For this workflow, the following code should also be chagned to handle Maybe type:

// From
import turn from './turn'

//reducer :: (AppState, Action a) -> AppState
const reducer = (prev, action) =>
  const result = turn(action)

  return isSameType(State, result)
    ? result.execWith(prev)
    : prev
}

export default reducer 

// To
import turn from './turn'

//reducer :: (AppState, Action a) -> AppState
const reducer = (prev, action) =>
  turn(action)
    .chain(safe(isSameType(State)))
//   ? result.execWith(prev)
// : prev export default reducer

Here ´turn(action)´ return a Maybe type, we still need to check whether inside Maybe, it is `State` type, 

.chain(safe(isSameType(State)))

If it is, then we call `execWith` otherwise we return previous state:

const reducer = (prev, action) =>
 turn(action)
   .chain(safe(isSameType(State)))
   .map(execWith(prev))
   .option(prev)

猜你喜欢

转载自www.cnblogs.com/Answer1215/p/11436596.html