React reducer 必须是纯函数吗? [英] Must a React reducer be a pure function?

查看:32
本文介绍了React reducer 必须是纯函数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个 UI 元素作为函数组件,它使用 React 的 userReducer 钩子,它似乎运行没有错误.

useReducer 引用了我写的一个函数(想象中叫做 reducer):

 const [state, dispatch] = React.useReducer(reducer, inputData,(输入数据) =>初始状态(输入数据));

state数据,由reducer函数输入输出;并且存在依赖于 state 的托管"UI 元素,例如 ...

 返回 (<div><div>{state.elements.map(getElement)}

<ShowHintshints={state.hints} inputValue={state.inputValue}/>

);

...这是正常的.

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

  • 它的行为只取决于它的输入参数——所以用相同的输入参数调用它两次会产生相同的结果.
  • 然而它会产生副作用,它不仅返回新状态

副作用是有一个 元素,其状态由以下之一控制:

const inputRef = React.createRef();

控件只是半托管的,就像这样:

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

onKeyDownonChange 事件是分派给 reducer 的动作(这很好),但是 reducer 被传递给了 HTMLInputElement 实例(即inputRef.current 值)作为输入参数,reducer 设置该 HTMLInputElement 的属性来改变它的状态——而不是 ; 是一个完全托管的组件,其内容由 reducer 的输出状态定义.

元素没有完全托管的原因是我需要控制选择范围(即 startend) 在 中,而不仅仅是它的文本值.

问题:

  • reducer 函数以这种方式不纯是否可以(例如,没有错误,尽管有问题或显着)?
    • 它只依赖于它的输入参数,因此是可重复的
    • 但它会改变某些东西(即 inputRef.current 属性)并返回新的 state
  • reducer 是否有另一种方法来控制 <input type="text">startend 属性元素,例如一种定义 元素的方法,使其 startend 值由 state 控制由减速器返回?

(我认为@Fyodor在下面的回答回答了第二个问题,我仍然不确定第一个问题).

<小时>

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

组件的设计和源代码此处显示,而且很长.

这是一个复杂的组件",它使用几个元素实现——几个

、几个、一些可点击的s 和 元素.

给出了reducer,作为它的输入参数:

  • 之前的状态"
  • 当前 实例(可以从中读取 的当前状态)
  • 由事件处理程序创建的动作"

多个事件处理程序或操作中的两个是 onKeyDownonChange 事件,因此当前状态 在发生改变 状态的事件时被传递给 reducer.

解决方案

从技术上讲,reducer 可能会产生不同的副作用.我认为这不是一个好的做法,至少由于关注点分离不好(预计reducer 只会根据动作产生新的状态,而不是改变其他东西).(抱歉,我提出了自己的观点,似乎使用了 useReducer 中的副作用,例如 此处).同样在 Redux 中,所有副作用都移至 动作创建者.

对于您的具体问题,我可能会建议将 inputRef 突变移动到单独的 useEffect 钩子,这又将取决于如下所示的状态

useEffect(() => {inputRef.currect//使用 inputRef}, [状态]);//依赖状态

这里是示例,制作useEffect依赖形式状态

您还可以将 useEffect 移动到 自定义钩子使代码可重用如下(未测试,用作提示)

function mutateRef (inputRef: React.RefObject, state:/* 状态类型 */) {useEffect(() => {inputRef.currect//使用 inputRef}, [状态, inputRef]);}

I wrote a UI element as a function component which uses React's userReducer hook and it seems to run without errors.

useReducer references a function I wrote (called, imaginatively, reducer):

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

There's state data which is input and output by the reducer function; and there are "managed" UI elements which depend on state, something like ...

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

... which is normal.

My concern is that the reducer function isn't pure.

  • Its behaviour depends only on its input parameters -- so calling it twice with the same input parameters has the same resulting effect.
  • However it produces a side-effect, it doesn't only return new state

The side-effect is that there is an <input> element whose state is controlled by one of these:

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

The <input> control is only semi-managed, something like this:

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

The onKeyDown and onChange events are actions dispatched to the reducer (which is good) but the reducer is passed the HTMLInputElement instance (i.e. the inputRef.current value) as an input parameter, and the reducer sets properties of that HTMLInputElement to mutate its state -- that is instead of the <input> being a fully-managed component whose content is defined by the state that's output from the reducer.

The reason why the <input> element isn't fully-managed is that I need to control the selection range (i.e. start and end) within the <input> and not only its text value.

Questions:

  • Is it OK (e.g. bug-free albeit questionable or remarkable) for a reducer function to be impure in this way?
    • It does only depend on its input parameters and is therefore repeatable
    • But it mutates something (i.e. inputRef.current properties) as well as returning new state value
  • Is there another way for a reducer to control the start and end properties of an <input type="text"> element, e.g. a way to define the <input> element such that its start and end values are controlled by he state returned by a reducer?

(I think @Fyodor's answer below answers the second question, I'm still not sure about the first question).


What dictates the values that are to be set on the HTML element? Does the use information passed in or does it contain the logic?

The component's design and source code are shown here, and quite long.

It's a complicated "component" which is implemented using several elements -- a couple of <div>s, several <span>s, some clickable <svg>s, and the <input> element.

The reducer is given, as its input parameters:

  • The previous "state"
  • The current <input> instance (from which it can read the current state of the <input>)
  • The "action" created by an event handler

Two of the several event handlers or actions are the onKeyDown and onChange events of the <input> so the current state of the <input> is passed-in to the reducer when there's an event which changes the state of the <input>.

解决方案

Technically reducer may work with different side effects in it. I don't think it is a good practice, at least due to bad separation of concerns (it is expected that reducer only produce new state based on action, but not mutate something else). (Sorry for presenting my own opinion, as it seems, that side effects in useReducer are used, like here). Also in Redux, all side effect are moved to action creators.

For your specific question I may recommend to move inputRef mutation to separate useEffect hook, which in turn will depends on state like below

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

Here is sample of making useEffect depended form state

You can also move useEffect to custom hook to make code reusable like below (not tested, use as hint)

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

这篇关于React reducer 必须是纯函数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆