Рамда: Как я могу сделать этот императивный редуктор более декларативным? - PullRequest
2 голосов
/ 23 июня 2019

У меня есть следующая функция редуктора:

Аргумент first для редукторов является агрегированным значением, а аргумент second является следующим значением. Приведенная ниже функция редуктора уменьшает тот же аргумент reaction, но агрегирует значение state$. Каждая функция редуктора дает новое агрегированное значение.

/**
 * Applies all the reducers to create a state object.
 */
function reactionReducer(reaction: ReactionObject): ReactionObject {
    let state$ = reactionDescriptionReducer({}, reaction);
    state$ = reactionDisabledReducer(state$, reaction);
    state$ = reactionIconReducer(state$, reaction);
    state$ = reactionOrderReducer(state$, reaction);
    state$ = reactionStyleReducer(state$, reaction);
    state$ = reactionTitleReducer(state$, reaction);
    state$ = reactionTooltipReducer(state$, reaction);
    state$ = reactionVisibleReducer(state$, reaction);
    return state$;
}

const state = reactionReducer(value);

Выше работает, но функция исправлена ​​ со списком редукторов. Кажется, что я должен быть в состоянии сделать что-то подобное с RamdaJS.

const state = R.????({}, value, [reactionDescriptionReducer
    reactionDisabledReducer,
    reactionIconReducer,
    reactionOrderReducer,
    reactionStyleReducer,
    reactionTitleReducer,
    reactionTooltipReducer,
    reactionVisibleReducer]);

Я новичок в RamdaJS, так что прости меня, если это нубский вопрос.

Как я могу выполнить цепочку редукторов, используя только RamdaJS?

Ответы [ 3 ]

5 голосов
/ 23 июня 2019

and создает новый редуктор, (r, x) => ..., путем объединения двух (2) входных редукторов, f и g -

const and = (f, g) =>
  (r, x) => g (f (r, x), x)

all, используя and, создает новый редуктор, комбинируя произвольное количество редукторов -

const identity = x =>
  x

const all = (f = identity, ...more) =>
  more .reduce (and, f)

Определите myReducer, используя all -

const myReducer =
  all
    ( reactionDisabledReducer
    , reactionIconReducer
    , reactionOrderReducer
    // ...
    )

С учетом имитации реализации этих трех (3) редукторов -

const reactionDisabledReducer = (s, x) =>
  x < 0
    ? { ...s, disabled: true }
    : s

const reactionIconReducer = (s, x) =>
  ({ ...s, icon: `${x}.png` })

const reactionOrderReducer = (s, x) =>
  x > 10
    ? { ...s, error: "over 10" }
    : s

Запустите myReducer, чтобы увидеть выходные данные

const initState =
  { foo: "bar" }

myReducer (initState, 10)
// { foo: 'bar', icon: '10.png' }

myReducer (initState, -1)
// { foo: 'bar', disabled: true, icon: '-1.png' }

myReducer (initState, 100)
// { foo: 'bar', icon: '100.png', error: 'over 10' }

Разверните фрагмент ниже, чтобы проверить результаты в своем браузере -

const identity = x =>
  x

const and = (f, g) =>
  (r, x) => g (f (r, x), x)

const all = (f, ...more) =>
  more .reduce (and, f)

const reactionDisabledReducer = (s, x) =>
  x < 0
    ? { ...s, disabled: true }
    : s

const reactionIconReducer = (s, x) =>
  ({ ...s, icon: `${x}.png` })

const reactionOrderReducer = (s, x) =>
  x > 10
    ? { ...s, error: "over 10" }
    : s

const myReducer =
  all
    ( reactionDisabledReducer
    , reactionIconReducer
    , reactionOrderReducer
    // ...
    )

const initState =
  { foo: "bar" }

console .log (myReducer (initState, 10))
// { foo: 'bar', icon: '10.png' }

console .log (myReducer (initState, -1))
// { foo: 'bar', disabled: true, icon: '-1.png' }

console .log (myReducer (initState, 100))
// { foo: 'bar', icon: '100.png', error: 'over 10' }

Вы можете выбрать любые имена, которые вам нравятся для and и all. Я мог видеть их как часть reducer модуля, как reducer.and и reducer.all

3 голосов
/ 24 июня 2019

Один из вариантов использования Ramda здесь - использовать тот факт, что он поддерживает передачу функций в качестве экземпляра монады в R.chain (иначе известный как монада Reader).

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

Мы можем использовать R.pipeWith(R.chain), чтобы позволить составить серию этих функций, которые принимают некоторый ввод (например, ваш $state пронизывает каждую функцию) и возвращают функцию, которая принимает среду, производя результат для передачи следующей функции в конвейере.

// Some mock functions to demonstrate

const reactionDescriptionReducer = ({...state}, reaction) =>
  ({ description: reaction, ...state })

const reactionDisabledReducer = ({...state}, reaction) =>
  ({ disabled: reaction, ...state })

const reactionIconReducer = ({...state}, reaction) =>
  ({ icon: reaction, ...state })

// effectively `R.pipeK`
const kleisli = R.pipeWith(R.chain)

// we need the functions going into chain to be curried
const curried = f => a => b => f(a, b)

// finally, compose the series of functions together
const reactReducer = kleisli([
  curried(reactionDescriptionReducer),
  curried(reactionDisabledReducer),
  curried(reactionIconReducer)
])({})

// and if all goes well...
console.log(
  reactReducer("someCommonReactionValue")
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
3 голосов
/ 23 июня 2019

Моя первая попытка совсем не связана с Рамдой, просто:

const makeReducer = (...fns) => (x) => fns .reduce ( (s, fn) => fn (s, x), {} )

const fn = makeReducer (
  (state$, reaction) => ({...state$, foo: `<<-${reaction.foo}->>`}),
  (state$, reaction) => ({...state$, bar: `=*=${reaction.bar}=*=`}),
  (state$, reaction) => ({...state$, baz: `-=-${reaction.baz}-=-`})
)

console .log (
  fn ( {foo: 'a', bar: 'b', baz: 'c'} )
) //~> {foo: '<<-a->>', bar: '=*=b=*=', baz: '-=-c-=-'}

Хотя вы можете использовать reduce и flip у Рамды, не похоже, что здесь они многое добавят.

...