我一直在努力找出管理我的反应表单的最佳方法。我试图使用onChange触发一个动作并用我的表单数据更新我的redux商店。我也试过创建本地状态,当我的表单被提交时,我触发并动作并更新redux存储。
我应该如何管理受控输入状态?
答案 0 :(得分:37)
我喜欢Redux的一位合着者的回答: https://github.com/reactjs/redux/issues/1287
将React用于与全局应用无关的短暂状态 并且不会以复杂的方式发生变异。例如,某些UI中的切换 元素,表单输入状态。将Redux用于全局重要的状态 或以复杂的方式变异。例如,缓存用户或帖子 。草案
有时你会想要从Redux状态转移到React状态(当时 在Redux中存储东西变得笨拙)或者相反(当时 更多组件需要访问以前的某个状态 本地)。
经验法则是:做任何不那么尴尬的事情。
也就是说,如果您确定您的表单不会影响全局状态,或者在卸载组件后需要保留,那么请保持处于反应状态。
答案 1 :(得分:27)
您可以使用组件自己的状态。然后采取该状态并将其作为行动的论据。这几乎就是" React方式"如React Docs。
您还可以查看Redux Form。它基本上完成了您所描述的内容,并将表单输入与Redux State链接。
第一种方式基本上意味着您手动完成所有操作 - 最大控制和最大样板。第二种方式意味着您让高阶组件为您完成所有工作。然后介于两者之间。我已经看到有多个软件包可以简化表单管理的特定方面:
React Forms - 它提供了一堆辅助组件,使表单呈现和验证更加简单。
React JSON schema - 允许用户从JSON模式构建HTML表单。
Formsy React - 正如描述所说:" React JS的这个扩展旨在成为"最佳点"在灵活性和可重用性之间。"
更新:似乎现在Redux Form已被替换为:
另一个值得检查的重要竞争者是:
答案 2 :(得分:6)
<强> TL; DR 强>
可以使用适合您应用的任何内容(来源:Redux docs)
确定应该是哪种数据的一些常用经验法则 投入Redux:
- 应用程序的其他部分是否关心此数据?
- 您是否需要能够根据此原始数据创建更多衍生数据?
- 是否使用相同的数据来驱动多个组件?
- 能否将此状态恢复到给定时间点(即时间旅行调试)是否对您有价值?
- 您是否要缓存数据(例如,如果已经存在,请使用状态,而不是重新请求它)?
这些问题可以轻松帮助您确定更适合您的应用的方法。以下是我在我的应用程序(表单)中使用的观点和方法:
input
(s)捕获数据并提交。我大部分时间都用这个简单的形式。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);
})
}