将自定义钩子添加到Evolution函数的优雅方法

时间:2019-09-29 12:08:58

标签: javascript reactjs functional-programming react-hooks ramda.js

我和我的团队每天都强烈采用FP和react-hooks模式,尽管有时我们在如何将钩子与纯函数一起管理上遇到困难。最近,我们遇到了很多情况,其中钩子“回调”必须位于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组件后初始化。到目前为止,在这些情况下该怎么做:

  • “动态”创建钩子函数,并将其传递给另一个ramda函数。例如{ const translate = useTranslate(); return R.compose(..., translate, ...) }-我们不喜欢这种方法,因为在执行此操作时我们无法合并无点样式; /

  • 另一种方法是以与我在上面的代码段中编写的相同的方式在匿名函数中调用hook。 -尽管我们不必编写整个功能块并声明一个附加变量,但我们也不喜欢这种方法,因为我们觉得,一直以来,我们都不得不多余地编写传递过来的参数:arg => hook()(arg)

FP中是否有任何通用方法来处理此类问题,因此我们必须将函数注入(必须对其进行延迟评估),然后根据该结果进行其他计算?甚至Ramda本身都具有可以注入特定行为的功能,因此我们可以这样使用它:

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

PS。我知道Ramda中有一个名为useWith的函数,但是它以不同的方式起作用; /

任何帮助都会得到高度赞赏!

1 个答案:

答案 0 :(得分:1)

我不确定我是否完全理解您的问题。您是否只是在寻找一种仅对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))

您需要做什么吗?