我正在使用Redux。我应该在Redux存储中管理受控输入状态还是在组件级别使用setState?

时间:2016-01-22 17:22:25

标签: reactjs redux redux-form

我一直在努力找出管理我的反应表单的最佳方法。我试图使用onChange触发一个动作并用我的表单数据更新我的redux商店。我也试过创建本地状态,当我的表单被提交时,我触发并动作并更新redux存储。

我应该如何管理受控输入状态?

5 个答案:

答案 0 :(得分:37)

我喜欢Redux的一位合着者的回答: https://github.com/reactjs/redux/issues/1287

  

将React用于与全局应用无关的短暂状态   并且不会以复杂的方式发生变异。例如,某些UI中的切换   元素,表单输入状态。将Redux用于全局重要的状态   或以复杂的方式变异。例如,缓存用户或帖子   。草案

     

有时你会想要从Redux状态转移到React状态(当时   在Redux中存储东西变得笨拙)或者相反(当时   更多组件需要访问以前的某个状态   本地)。

     

经验法则是:做任何不那么尴尬的事情。

也就是说,如果您确定您的表单不会影响全局状态,或者在卸载组件后需要保留,那么请保持处于反应状态。

答案 1 :(得分:27)

  1. 您可以使用组件自己的状态。然后采取该状态并将其作为行动的论据。这几乎就是" React方式"如React Docs

  2. 中所述
  3. 您还可以查看Redux Form。它基本上完成了您所描述的内容,并将表单输入与Redux State链接。

  4. 第一种方式基本上意味着您手动完成所有操作 - 最大控制和最大样板。第二种方式意味着您让高阶组件为您完成所有工作。然后介于两者之间。我已经看到有多个软件包可以简化表单管理的特定方面:

    1. React Forms - 它提供了一堆辅助组件,使表单呈现和验证更加简单。

    2. React JSON schema - 允许用户从JSON模式构建HTML表单。

    3. Formsy React - 正如描述所说:" React JS的这个扩展旨在成为"最佳点"在灵活性和可重用性之间。"

    4. 更新:似乎现在Redux Form已被替换为:

      1. React Final Form
      2. 另一个值得检查的重要竞争者是:

        1. Formik

答案 2 :(得分:6)

<强> TL; DR

可以使用适合您应用的任何内容(来源:Redux docs

  

确定应该是哪种数据的一些常用经验法则   投入Redux:

     
      
  • 应用程序的其他部分是否关心此数据?
  •   
  • 您是否需要能够根据此原始数据创建更多衍生数据?
  •   
  • 是否使用相同的数据来驱动多个组件?
  •   
  • 能否将此状态恢复到给定时间点(即时间旅行调试)是否对您有价值?
  •   
  • 您是否要缓存数据(例如,如果已经存在,请使用状态,而不是重新请求它)?
  •   

这些问题可以轻松帮助您确定更适合您的应用的方法。以下是我在我的应用程序(表单)中使用的观点和方法:

当地州

  • 当我的表单与UI的其他组件无关时很有用。只需从input(s)捕获数据并提交。我大部分时间都用这个简单的形式。
  • 我没有在时间旅行中看到很多用例调试表单的输入流(除非其他一些UI组件依赖于此)。

Redux状态

  • 当表单必须更新我的应用程序中的其他UI组件时很有用(非常像two-way binding)。
  • 当我的表单input(s)导致某些其他组件render取决于用户输入的内容时,我会使用此功能。

答案 3 :(得分:4)

就个人而言,我强烈建议将所有内容保持在Redux状态并远离本地组件状态。这主要是因为如果你开始将ui视为状态函数,你可以完成无浏览器测试,你可以利用保持完整状态历史的参考(例如,输入中的内容,打开的对话框等等)当用户出于调试目的时,一个错误命中 - 不是从一开始就是什么状态)。 Related tweet from the realm of clojure

编辑添加:这是我们和我们的姐妹公司在我们的生产应用程序和我们如何处理redux / state / ui

方面的进展

答案 4 :(得分:0)

使用帮助程序库更加快捷,并且避免了我们所有的样板工作。它们可能已经过优化,功能丰富...等等。由于它们使所有不同方面更加轻而易举。测试它们并使您的武器库了解满足不同需求的有用和更好的东西,这就是要做的事情。

但是如果您已经自己实现了一切。采用受控方式。由于某种原因,您需要还原。在我的一个项目中。我需要维护表单状态。因此,如果我转到另一个页面并返回,它将保持相同状态。如果这是将更改传达给多个组件的一种方式,则仅需要redux。或者,如果这是存储状态的一种手段,则需要还原。

如果状态需要是全局的,则需要redux。否则,您将不需要它。因此,您可以查看这篇出色的文章here,以进行深入了解。

您可能遇到的问题之一!使用受控输入时。是您可以只是在每次击键时发送更改。您的表格将开始冻结。它变成了蜗牛。

在任何更改时,都不要直接调度和使用redux流量。 您可以做的是将输入状态存储在组件本地状态中。并使用setState()更新。状态更改后,您将设置一个带有延迟的计时器。每次击键都会取消它。在指定的延迟后,最后一次击键后将执行调度操作。(一个好的延迟可能是500ms)。

(知道setState,默认情况下有效地处理了多个连续的击键。否则,我们将使用与上述相同的技术(就像我们在vanilla js中所做的一样)[但是这里我们将依靠{{1 }}])

以下是一个示例:

setState

您可以使用反模式使用以下道具来初始化组件:

onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        // here how you implement the delay timer
        clearTimeout(this.onInputsChangeTimeoutHandler); // we clear at ever keystroke
              // this handler is declared in the constructor
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
           // this will be executed only after the last keystroke (following the delay)
            if (typeof onInputsChange === "function")
                    onInputsChange(this.state.inputs, roomId, evt);
        }, 500);
    })
}

在构造函数中或在constructor(props) { super(props); const { name, description } = this.props; this.state = { inputs: { name, description } } 挂钩中,如下所示:

componentDidMount

稍后,每次安装组件时,我们都可以从存储中恢复状态。

此外,如果需要从父组件更改表单,则可以向该父组件公开一个函数。通过设置绑定componentDidMount () { const { name, description } = this.props; this.setState({ ...this.state, inputs: { name, description } }); } 方法。然后在构造中执行道具(这是一种吸气方法)setInputs()。 (一个有用的情况是,当您想在某些条件或状态下重置表单时。)

getSetInputs()

要更好地理解我在上面所做的工作,请在此处更新输入内容:

constructor(props) {
    super(props);
    const {
         getSetInputs
    } = this.props;

   // .....
   if (typeof getSetInputs === 'function') getSetInputs(this.setInputs);
}

这是我的表格:

// inputs change handlers
onNameChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            name: value
        },
        evt
    );
}

onDescriptionChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            description: value
        },
        evt
    );
}

/**
 * change = {
 *      name: value
 * }
 */
onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        clearTimeout(this.onInputsChangeTimeoutHandler);
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
            if (typeof onInputsChange === "function")
                onInputsChange(change, roomId, evt);
        }, 500);
    })
}