Как бы вы написали условие в рамде? - PullRequest
0 голосов
/ 19 мая 2018

Я новичок в Рамде и просто пытаюсь обернуть голову вокруг него.Итак, вот функция, которую я хочу переписать в функциональном стиле:

const makeReducer = (state, action) => {
  if (action.type === LOG_OUT) {
    return rootReducer(undefined, action)
  }
  return rootReducer(state, action)
}

Вот что я получаю в итоге:

const isAction = type => R.compose(R.equals(type), R.prop('type'))

const makeReducer = (state, action) => {
  const isLogOut = isAction(LOG_OUT)
  return R.ifElse(isLogOut, rootReducer(undefined, action), rootReducer(state, action))(action)
}

Я предполагаю, что это совершенно неправильно, поскольку есть несколько дубликатовaction и rootReducer

Ответы [ 3 ]

0 голосов
/ 23 мая 2018

Если вы хотите использовать синтаксис стиля pointfree для своего кода, вы можете сделать что-то вроде:

const initialState = {
  text: 'initial text'
}

const rootReducer = R.curry((state, action) => {
  // setting initial state could be improved
  state = state || initialState
  // your root reducer logic here
  return state;
})

// R.last is here to grab the action in [state, action]
const isAction = type => R.compose(R.equals(type), R.prop('type'), R.last)

// first makes (state, action) into [state, action]
// before running R.cond
const makeReducer = R.compose(R.cond([
  [isAction('LOG_OUT'), R.compose(rootReducer(undefined), R.last)],
  // "default" action
  [R.T, R.apply(rootReducer)]
]), R.pair)

const loggedOutState = makeReducer(
  { text: 'latest text'},
  { type: 'LOG_OUT'}
)

console.log(loggedOutState)
// => { text: 'initial text' }

const nextState = makeReducer(
  { text: 'latest text'},
  { type: 'ANY_ACTION'}
)

console.log(nextState)
// => { text: 'latest text' }
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

Что хорошо в этом решении, так это то, что вы можете легко расширять makeReducer для обработки большего количества действий (поскольку он использует R.cond - что похоже на переключательзаявление).

0 голосов
/ 23 мая 2018

Вы можете легко избавиться от дублирования:

const makeReducer = (state, action) => 
  rootReducer((action.type === LOG_OUT ? undefined : state), action)

Это действительно ни более, ни менее функционально, чем оригинал.Но у него есть преимущество, заключающееся в уменьшении дублирования и в работе только с выражениями, а не с утверждениями, что иногда является проблемой функциональных методов.

Но есть один способ, которым он явно не функционален.В вашем коде есть свободная переменная: LOG_OUT.Я предполагаю из ALL_CAPS, что это должно быть константой.Но функция не знает этого.Так что эта функция на самом деле не является ссылочно прозрачной.Возможно, что между вызовами с одинаковыми параметрами кто-то изменит значение LOG_OUT, и вы можете получить разные результаты.

Это затрудняет проверку функции.(Вы не можете просто предоставить ему необходимые параметры; вы также должны иметь правильное значение LOG_OUT в области видимости.) И это усложняет рассуждение.

Альтернативой без этой проблемы является

const makeReducer = (state, action, types) => 
  rootReducer((action.type === types.LOG_OUT ? undefined : state), action)
0 голосов
/ 19 мая 2018

На самом деле я не вижу причин для рефакторинга вашего кода: вы не изменяете входные данные и используете if для условного возврата выходных данных.

О rootReducer(undefined, action), я считаю, что вы должны использоватьдеструктуризация параметров:

const rootReducer = ({ state, action } = {}} => {
   // Stuff here
}

То есть вы можете указать либо state, либо action, либо и то и другое:

const makeReducer = ({ state, action }) => {
  if (action.type === LOG_OUT) {
    return rootReducer({ action })
  }
  return rootReducer({ state, action })
}

Кроме того, рассмотрите возможность использования троицы для решения простых случаев:

const makeReducer = ({ state, action }) =>
     rootReducer( action.type === LOG_OUT ? { action } : { state, action } )

Наконец, может быть еще один подход с использованием теговых сумм и сгибов.Поскольку я не работаю с React и / или Redux , я не знаю, можно ли использовать этот подход, но я считаю, что все еще интересно, что вы обнаружили это альтернативное решение:

const tag = Symbol ( 'tag' )

// TaggedSum
const Action = {
    logout: value => ( { [tag]: 'logout', value } ),
    login: value => ( { [tag]: 'login', value } )
}

const foldAction = matches => action => {
   const actionTag = action[ tag ]
   const match = matches [ actionTag ]
   
   return match ( action.value )
}

const state = { x: 1 }
const LOG_IN = 1
const LOG_OUT = 2
const logout = Action.logout ( { action: LOG_OUT, state } )
const login = Action.login ( { action: LOG_IN, state } )

const rootReducer = args => console.log ( args )

// Pattern matching
const matchAction = {
   logout: ( { state } ) => rootReducer( { state } ),
   login: rootReducer
}

const foldAction_ = foldAction( matchAction )

foldAction_ ( logout )
foldAction_ ( login )
...