Элегантный способ ввести пользовательский крючок в функцию эволюции - PullRequest
1 голос
/ 29 сентября 2019

Моя команда и я ежедневно активно применяем паттерны FP и реагирующих хуков, хотя иногда мы боремся, как управлять хуками вместе с чистыми функциями.В последнее время мы сталкиваемся с множеством ситуаций, когда «обратный вызов» ловушки должен находиться где-то внутри compose или другого преобразователя - в данном случае это функция evolve, например:

const getDefinedActions = R.filter(
      R.both(isActionDefined, R.ifElse(hasAfterConfirm, isAfterConfirmDefined, R.T))
    )
    
const translateTitles = R.map(
  R.evolve({
    title: title => useTranslate()(title)
  })
)

export const useActions = R.compose(
  translateTitles,
  getDefinedActions
)

В приведенном выше примере мы хотим отфильтровать действия на основе некоторых предикатов + добавить переводы, которые требуют использования хука useTranslate.К сожалению, мы не можем просто написать useTranslate() там, потому что ловушка должна быть инициализирована после монтирования компонента React.Что было сделано до сих пор в таких ситуациях:

  • создать функцию ловушки «на лету» и передать ее функции другой рамды.например, { const translate = useTranslate(); return R.compose(..., translate, ...) } - нам не нравится этот подход, потому что мы не можем включить стиль без точек при этом; /

  • другой способ - вызвать хук в анонимной функции втак же, как я написал во фрагменте выше.- хотя нам не нужно писать весь функциональный блок и объявлять дополнительную переменную, этот подход нам тоже не нравится, потому что нам все время приходится избыточно записывать передаваемые аргументы: arg => hook()(arg)

Существует ли какой-либо общий подход в FP для решения такого рода проблемы, когда мы должны внедрить функцию, которую нужно лениво оценить, а затем на основе этого результата мы можем провести другие вычисления?Может быть, даже у Ramda есть функция, которая помогает вводить определенное поведение, поэтому мы можем использовать ее следующим образом:

R.evolve({
  title: R.useWith(useTranslate, R.identity) // arg => useTranslate()(R.identity(arg))
})

PS.Я знаю, что в Ramda есть функция useWith, но она работает по-другому; /

Любая помощь будет высоко оценена!

1 Ответ

1 голос
/ 29 сентября 2019

Я не уверен, что полностью понимаю ваш вопрос.Вы просто ищете способ выполнить инициализацию useTranslate только один раз?Если так, то я не думаю, что в Ramda есть что-то встроенное, но было бы достаточно легко написать что-то вроде

const dethunk = (fn) => {
  let initialized = null;
  return (...args) => (initialized || (initialized = fn())) (...args)
}

, а затем написать что-то вроде.

const translateTitles = map (evolve ({title: dethunk(useTranslate) }))

Иесли вы хотите передать параметры инициализации, вы можете изменить его на

const dethunk = (fn, ...initialArgs) => {
  let initialized = null;
  return (...args) => (initialized || (initialized = fn(...initialArgs))) (...args)
}

const foo = (a, b) => {
  console.log(`Initializing with '${a}' and '${b}'`)
  return (c) => `foo('${a}', '${b}', ${c})`
}

const bar = dethunk(foo, 'x', 'y')

console.log('Have not initialized yet');

console .log (bar (1))
console .log (bar (2))
console .log (bar (3))

Делает ли это то, что вам нужно?

...