Ramda:有没有办法在管道过程中将参数“分叉”给两个函数?

时间:2019-02-20 13:02:52

标签: javascript redux functional-programming ramda.js

我是函数编程初学者。我正在使用Ramda开发一个React Native应用程序。该应用程序允许用户维护房屋。

我编写了名为asyncPipe的函数,该函数使我可以传递诺言和常规函数。我将其用于loginFlow,该请求当前具有http请求(getHouseList)作为其最后一个功能。

const asyncPipe = (...fns) => x => fns.reduce(async (y, f) => f(await y), x);

const loginFlow = asyncPipe(
  // ... someFunctions
  getHouseList
);

// used later like this in LoginForm.js's handleSubmit():
const list = await loginFlow(credentials);

因此,登录后,该应用程序将加载用户的房屋。现在,根据他是只有一栋还是多栋房屋,我想向用户发送列表视图以选择房屋,或者如果他只有一栋房屋,则向用户发送详细视图。另外,我想分派Redux操作以将列表保存在我的reducer中,如果要进行分派,则要分派另一个操作。

目前我是这样的:

const list = await loginFlow(credentials);
dispatch(addHouses(list));
if (list.length > 1) {
  navigate('ListScreen')
} else {
  dispatch(pickHouse(list[0]);
  navigate('DetailScreen') ;
}

但是正如您所看到的那样,这势在必行。似乎我必须“分叉”列表并在管道中使用两次(因为Redux'dispatch没有返回值)。

我的主要问题是:

如何(如果可以的话)使它更具功能性?

我要问的一个小问题是,是否可以在这里/是否使其正常工作是个好主意。

2 个答案:

答案 0 :(得分:2)

您可以使用tap之类的东西来扩展异步管道:

const loginFlow = asyncPipe(
  // ... some functions
  getHouseList,
  tap(compose(dispatch, addHouses)),
  tap(unless(list => list.length > 1, list => dispatch(pickHouse(list[0])))),
  list => navigate(list.length > 1 ? 'ListScreen' : 'DetailScreen', list)
);

这是否值得取决于您的应用程序。如果管道已经很长,那么以这种方式添加内容可能会更干净,即使它们不是特别有用的部分。但是对于较短的管道,这可能没有多大意义。

您可能还想看看现在已弃用的pipeP或它的替代版本pipeWiththen)。

但是您在标题中询问了有关派生参数的问题。 Ramda的converge正是这样做的:

converge(f, [g, h])(x) //=> f(g(x), h(x))

这还允许您传递两个以上的函数,并将多个参数传递给结果函数:

converge(f, [g, h, i])(x, y) //=> f(g(x, y), h(x, y), i(x, y)) 

答案 1 :(得分:1)

鉴于我们可以使用R.thenR.otherwise,那么实际上并不需要asyncPipe。函数式编程的原理之一实际上是委托编排...

最后,您当然可以声明更多,并且一个好的开始方法是尝试避免命令式的控制流。 R.ifElse绝对会在这里为您提供帮助:)

如果您的代码有副作用, 然后在管道中使用R.tap:)

const fake = cb => () => cb([
  { name: 'Hitmands', id: 1 },
  { name: 'Giuseppe', id: 2 },
]);

const fakeApiCall = () => new Promise(resolve => setTimeout(fake(resolve), 500));
const dispatch = action => data => console.log(`dispatch("${action}")`, data);
const navigate = view => data => console.log(`navigate("${view}")`, data);

const loginFlow = (...fns) => R.pipe(
  R.tap(() => console.log('login Flow Start')),
  fakeApiCall,
  R.then(R.pipe(
    ...fns,
    R.tap(() => console.log('login Flow End')),
  )),
)

const flow = loginFlow(
  R.tap(dispatch('addHouse')), // use tap for side effects
  R.ifElse(
    R.pipe(R.length, R.gt(R.__, 1)), // result.length > 1
    R.tap(navigate('ListScreen')), // onTrue
    R.pipe( // onFalse
      R.tap(dispatch('pickHouse')),
      R.tap(navigate('DetailScreen')),
    ),
  ),
);

/* await */ flow();

/** UPDATES **/
const isXGreaterThan1 = R.gt(R.__, 1);
const isListLengthGreatherThanOne = R.pipe(R.length, isXGreaterThan1);

console.log(`is list.length > 1`, isListLengthGreatherThanOne([1, 2, 3]));
console.log(`is list.length > 1`, isListLengthGreatherThanOne([1]));
console.log(`is list.length > 1`, isListLengthGreatherThanOne([]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

相关问题