在React.js表单组件中使用状态或引用?


116

我从React.js开始,我想做一个简单的表格,但是在文档中我找到了两种方法。

第一种是使用参考文献

var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="Your name" ref="author" />
        <input type="text" placeholder="Say something..." ref="text" />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

第二个是使用状态内的阵营组分:

var TodoTextInput = React.createClass({
  getInitialState: function() {
    return {
      value: this.props.value || ''
    };
  },

  render: function() /*object*/ {
    return (
      <input className={this.props.className}
      id={this.props.id}
      placeholder={this.props.placeholder}
      onBlur={this._save}
      value={this.state.value}
      />
    );
  },

  _save: function() {
    this.props.onSave(this.state.value);
    this.setState({value: ''
  });
});

如果存在,我看不到这两种选择的优缺点。谢谢。


我在这里想念什么吗?为什么不使用事件对象获取表单值?这似乎是首先在这里使用表格的唯一原因。如果您没有使用默认的提交行为,并且对输入具有引用,则无需将其包装为表单。
NectarSoft '16

Answers:


143

简短版本:避免引用。


它们不利于可维护性,并且失去了所见即所得模型渲染所提供的许多简单性。

您有一个表格。您需要添加一个按钮来重置表单。

  • 参考:
    • 操作DOM
    • render描述了3分钟前表单的外观
    • setState
    • 渲染描述表单外观

您在输入中有一个CCV数字字段,而在应用程序中还有一些其他数字字段。现在您需要强制用户仅输入数字。

  • 参考:
    • 添加一个onChange处理程序(我们不是使用引用来避免这种情况吗?)
    • 如果不是数字,则在onChange中操纵dom
    • 您已经有一个onChange处理程序
    • 添加一条if语句,如果无效,则不执行任何操作
    • 仅在将产生不同结果时才调用render

嗯,没关系,项目经理希望我们在无效的情况下只绘制一个红色的阴影。

  • 参考:
    • 使onChange处理程序仅调用forceUpdate之类的东西?
    • 使渲染输出基于...,是吗?
    • 我们从哪里获得在渲染中验证的值?
    • 手动操作元素的className dom属性?
    • 我迷路了
    • 没有参考重写?
    • 从渲染中的dom中读取(如果已安装),否则假定有效?
  • 州:
    • 删除if语句
    • 使渲染基于this.state验证

我们需要将控制权交还给父母。数据现在在道具中,我们需要对更改做出反应。

  • 参考:
    • 实现componentDidMount,componentWillUpdate和componentDidUpdate
    • 手动比较以前的道具
    • 以最小的变化来操纵dom
    • 嘿!我们正在实施反应中的反应...
    • 还有更多,但我的手指受伤
  • 州:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

人们认为裁判比保持状态更“容易”。这可能在最初的20分钟内是正确的,但根据我的经验,那是不正确的。让您自己说“是的,我会在5分钟内完成”,而不是“当然,我会重写一些组件”。


3
您能否进一步解释sed -e's / this.state / this.props /''s / handleChange / onChange /'-i form.js?
gabrielgiussi 2015年

1
不,我指的是对dom的实际更改。 React.findDOMNode(this.refs.foo)。如果您进行更改,则this.refs.foo.props.bar不会发生任何事情。
Brigand

1
例如,如果您将其 <input onChange={this.handleChange} value={this.state.foo} />更改为<input onChange={this.props.handleChange} value={this.props.foo} />,或修改handleChange函数以调用props中的回调。无论哪种方式,它都是一些小的明显变化。
Brigand

4
不知道我是否是唯一找到您的答案的人。您可以显示一些代码示例使您的观点更清楚吗?
利沙伯

2
在屏幕上具有50个以上的输入以呈现任何状态变化都是不希望的。input理想的情况是将每个字段组成部分,每个字段都保持自己的状态。在某些时候,我们确实需要使用更大的模型来协调这些独立的状态。也许我们在计时器上有自动保存功能,或者我们只是保存了。componentWillUnmount这是我认为很refs理想的地方,在对帐期间,我们state从每个取值ref,但没有一个更明智。我同意在大多数情况下state是解决方案,但是使用大量inputs适当的refs模式将为性能带来收益
lux

105

我已经看到一些人引用以上答案作为“从不使用refs”的原因,我想发表自己的观点(以及与我交谈过的其他一些React开发人员)。

在谈论将其用于组件实例时,“不要使用引用”是正确的。意思是,您不应使用引用作为获取组件实例并在其上调用方法的方法。这是使用裁判的不正确方法,并且是裁判很快走到南边的时候。

正确(且非常有用)的使用引用是当您使用引用从DOM中获取一些价值时。例如,如果您有一个将引用附加到该输入的输入字段,则稍后通过该引用获取值就可以了。如果没有这种方式,您就需要经历一个精心策划的过程,以使您的输入字段与您的本地州或流量存储区保持最新状态-这似乎是不必要的。

2019编辑:未来的朋友你好。除了我几年前提到的^之外,借助React Hooks,引用也是跟踪渲染之间的数据的一种好方法,而不仅限于获取DOM节点。


3
您的最后一段很合理,但是您可以澄清第二段吗?抓住组件实例并调用被认为不正确的方法的具体示例是什么?
Danny Libin

2
我同意这一点。我使用引用,除非/直到我需要验证或操纵字段的值。如果我确实需要对更改进行验证或以编程方式更改值,则可以使用状态。
Christopher Davies

1
我也同意这一点。在发现阶段,我特意接近了一个天真的带有大量输入的屏幕。存储在映射中的所有输入值(处于状态),由ID键控。毋庸置疑,由于设置状态并在诸如复选框单击之类的较小UI更改上渲染50多个输入(某些Material-UI,这很重!),性能因此受到影响,这并不是理想的选择。组成每个可以保持自己状态的输入似乎是正确的方法。如果需要对帐,只需查看refs并获取状态值。实际上,这似乎是一个非常好的模式。
勒克斯

2
我完全同意。我认为,已接受的答案太含糊。
James Wright

我同意。在设计通用Form组件时,这可以揭示受控组件和管理焦点,错误处理等方面的痛点。实际上不可能有一个干净的架构。如果需要,请与我联系。我正在将组件移动到引用。
kushalvm

6

TL; DR一般来说,refs与React的声明式哲学背道而驰,因此您应将它们用作最后​​的选择。state / props尽可能使用。


为了了解您在哪里使用refsvs state / props,让我们看一下React遵循的一些设计原则。

每个React 文档关于refs

避免将ref用于可以声明式完成的任何事情。

Per React关于逃生舱口的设计原则

如果很难以声明的方式表达一些对构建应用有用的模式,那么我们将为其提供命令性的API。(他们链接到这里的裁判)

这意味着React的团队建议避免refsstate / props以反应性/声明性方式进行任何使用。

@Tyler McGinnis提供了一个很好的答案,并指出

正确(且非常有用)的使用引用的方法是当您使用它们从DOM中获取一些价值时...

尽管可以做到这一点,但是您将与React的理念背道而驰。如果您在输入中有价值,那么它肯定来自state / props。为了保持代码的一致性和可预测性,您还应该坚持在state / props那里。我承认refs有时可以为您提供更快的解决方案这一事实,因此,如果您进行概念验证,则可以快速而又肮脏地接受。

这给我们留下了几个具体的使用情况进行refs

管理焦点,文本选择或媒体播放。触发命令式动画。与第三方DOM库集成。


5

这个帖子很旧。

在这件事上,我将分享我的一点经验。

我正在开发一个大型组件(414行),其中包含许多“动态”输入和涉及的许多缓存数据。(我不是在页面上一个人工作,我的感觉告诉我,代码的结构可能可以更好地拆分,但这不是重点(嗯,可以,但是我正在处理)

我首先使用state处理输入的值:

  const [inputsValues, setInputsValues] = useState([])
  const setInputValue = (id, value) => {
    const arr = [...inputsValues]
    arr[id] = value
    setInputsValues(arr)
  }

当然在输入中:

value={inputsValues[id] || ''}
onChange={event => setInputValue(id, event.target.value)}

渲染是如此沉重,以至于输入更改不连贯,如****(不要试图按住键,文本只会在暂停后出现)

我确信我可以使用ref来避免这种情况。

最终像这样:

  const inputsRef = useRef([])

并在输入中:

ref={input => (inputsRef.current[id] = input)}

[在我的情况下,输入是Material-UI TextField,所以它是:

inputRef={input => (inputsRef.current[id] = input)}

]

有了这个,没有渲染,输入很流畅,相同。它将节省周期和计算,因此也节省了能源。为地球做x)

我的结论是:甚至可能需要useRef作为输入值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.