在 Ramda 中思考

Thinking in Ramda

我知道 Ramda 有很多有用的功能,但我不知道,如何组合它比这个更好。有人可以做得更好吗? PS。参考一下 Ramda 中的 think 就好了。

// @flow
/**
  const state = {
    forms: [
      {id: 'form-id'}
    ],
    inputs: [
      {inputId: 'input-id', formId: 'form-id', value: ''}
    ]
  }
*/


import { propEq, find, update, compose, assoc, assocPath, findIndex } from 'ramda';

export const setInputValueInState = (
  inputId: string,
  value: string,
  state: FormsState,
): FormsState => {
  const input = getInputById(inputId, state);
  const inputWithNewValue = assoc('value', value, input);
  const inputIndex = findIndex(propEq('inputId', inputId))(state.inputs);
  return assocPath(['inputs', inputIndex], inputWithNewValue, state);
};

有很多方法可以解决这个问题。通过巧妙地使用 useWith or converge,您可能可以创建现有函数的无点版本。不过,我不认为它最终会变得特别可读,所以我会寻找其他技术。

一种可能是使用lenses。大多数关于镜头的文章都适用于 Haskell 或其他语言。但我在 A. Sharif, Drew Tipson, and Vladimir Gorej

的 Javascript 中找到了一些

Ramda 有几个内置镜头创建功能,lensProp, lensPath, and lensIndex, as well as functions like view, set, and over 可以使用镜头。但是创建您自己的也很容易,例如下面的 inputIdLens

const state = {
  forms: [
    {id: 'form-id'}
  ],
  inputs: [
    {inputId: 'i-1', formId: 'foo-12', value: '123'},
    {inputId: 'i-2', formId: 'bar-34', value: '234'},
    {inputId: 'i-3', formId: 'baz-56', value: '345'},
    {inputId: 'i-4', formId: 'qux-78', value: '456'},
    {inputId: 'i-5', formId: 'corge-90', value: '567'}
  ]
}

const {lens, find, propEq, update, findIndex, set, compose, lensProp} = R

const inputIdLens = (inputId) => lens(
  (s) => find(propEq('inputId', inputId), s),
  (a, s) => update(findIndex(propEq('inputId', inputId), s), a, s)
)

const setInputValueInState = (inputId, val, state) => 
  set(compose(lensProp('inputs'), inputIdLens(inputId), lensProp('value')), val, state)

console.log(setInputValueInState('i-2', 'foobar', state))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

尽管 Scott 的回答感觉更实用,但我发现这种方法更易于编写和理解。我更喜欢 evolve 而不是镜头,IMO 感觉更自然。

import { curry, evolve, adjust, findIndex, propEq, assoc } from 'ramda';

const state = {
  forms: [
    {id: 'form-id'}
  ],
  inputs: [
    {inputId: 'i-1', formId: 'form-1', value: '123'},
    {inputId: 'i-2', formId: 'form-2', value: '234'},
  ]
}

const setInputValueInState = curry((inputId, value, obj) => evolve({
  inputs: adjust(
    findIndex(propEq('inputId', inputId), obj.inputs), 
    assoc('value', value),
  ),
}, obj));

setInputValueInState('i-2', 'foobar', state);