React Reducer必须是纯函数吗?

时间:2019-07-22 08:40:22

标签: reactjs typescript react-hooks pure-function

我编写了一个UI元素作为功能组件,该元素使用React的userReducer钩子,并且似乎可以正常运行。

useReducer引用了我编写的函数(想象中称为reducer):

  const [state, dispatch] = React.useReducer(reducer, inputData,
    (inputData) => initialState(inputData));

state数据,由化简函数输入和输出;还有依赖于state的“托管” UI元素,例如...

  return (
    <div>
      <div>
        {state.elements.map(getElement)}
      </div>
      <ShowHints hints={state.hints} inputValue={state.inputValue} />
    </div>
  );

...这很正常。

我担心的是reducer函数不是纯函数。

  • 它的行为仅取决于其输入参数-因此,使用相同的输入参数两次调用它会产生相同的结果。
  • 但是它产生副作用,不仅会返回新状态

副作用是存在一个<input>元素,其状态由以下其中之一控制:

const inputRef = React.createRef<HTMLInputElement>();

<input>控件仅是半托管的,如下所示:

<input type="text" ref={inputRef} onKeyDown={handleKeyDown} onChange={handleChange}

onKeyDownonChange事件是分派给reducer的动作(很好),但是reducer传递给HTMLInputElement实例(即inputRef.current值)为输入参数,然后reducer设置该HTMLInputElement的属性以使其状态发生变化-而不是<input>是一个完全托管的组件,其内容由reducer输出的状态定义

<input>元素未得到完全管理的原因是,我需要控制startend中的选择范围(即<input>inputRef.current)不仅是文本值。

问题:

  • 以这种方式不纯的reduce函数是否可以正常工作(例如,没有问题或令人毛骨悚然)?
    • 它仅取决于其输入参数,因此是可重复的
    • 但是它会改变某些内容(即state属性)并返回新的start
  • Reducer是否有另一种方式来控制end元素的<input type="text"><input>属性,例如一种定义start元素的方法,以便其endstate的值由化简器返回的<div>控制?

(我认为@Fyodor's answer below回答了第二个问题,但我仍然不确定第一个问题)。


是什么决定了要在HTML元素上设置的值?传入的使用信息还是包含逻辑?

该组件的设计和源代码很长shown here

这是一个复杂的“组件”,它使用几个元素来实现-几个<span>,几个<svg>,一些可点击的<input>和{{1} }元素。

给出减速器作为其输入参数:

  • 上一个“状态”
  • 当前<input>实例(可以从中读取<input>的当前状态)
  • 事件处理程序创建的“动作”

几个事件处理程序或操作中的两个是onKeyDown的{​​{1}}和onChange事件,因此<input>的当前状态被传递给化简器当发生更改<input>状态的事件时。

1 个答案:

答案 0 :(得分:1)

技术上,还原剂可能在其中具有不同的副作用。我认为这不是一个好的做法,至少是由于关注点之间的分离不良(可以期望reducer仅基于操作产生新的状态,而不会改变其他状态)。 (很抱歉,我提出自己的见解,似乎使用了useReducer中的副作用,例如here)。同样在Redux中,所有副作用都移至action creators

对于您的特定问题,我建议您将inputRef突变移动到单独的useEffect钩子上,这又取决于下面的状态

useEffect (() => {
    inputRef.currect // do work with inputRef
}, [state]); // make dependent from state

sampleuseEffect依赖形式state

您还可以将useEffect移至custom hook,以使代码可重复使用,如下所示(未经测试,用作提示)

function mutateRef (inputRef: React.RefObject<HTMLInputElement>, state: /* type of state */) {
    useEffect (() => {
        inputRef.currect // do work with inputRef
    }, [state, inputRef]);
}