【问题标题】:Use state or refs in React.js form components?在 React.js 表单组件中使用 state 或 refs?
【发布时间】:2015-06-12 17:39:54
【问题描述】:

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

first one 正在使用 Refs

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>
    );
  }
});

second one 在 React 组件中使用 state

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: ''
  });
});

如果有的话,我看不出这两种选择的优缺点。 谢谢。

【问题讨论】:

  • 我在这里遗漏了什么吗?为什么不使用事件对象来获取表单值?这似乎是首先在这里使用表格的唯一原因。如果您没有使用默认提交行为并且在输入上有引用,则不需要将它们包装在表单中。

标签: reactjs


【解决方案1】:

简短版本:避免引用。


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

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

  • 参考:
    • 操作 DOM
    • render 描述了表单在 3 分钟前的样子
  • 状态
    • 设置状态
    • render 描述了表单的外观

您的输入中有一个 CCV 编号字段,而您的应用程序中有一些其他字段是数字。现在你需要强制用户只输入数字。

  • 参考:
    • 添加一个 onChange 处理程序(我们不是使用 refs 来避免这种情况吗?)
    • 如果不是数字,则在 onChange 中操作 dom
  • 状态
    • 您已经有一个 onChange 处理程序
    • 添加 if 语句,如果无效则什么都不做
    • 只有在要产生不同结果时才会调用渲染

呃,没关系,如果它无效,PM 想要我们只做一个红色框阴影。

  • 参考:
    • 让 onChange 处理程序只调用 forceUpdate 什么的?
    • 根据...制作渲染输出是吧?
    • 我们从哪里获得要在渲染中验证的值?
    • 手动操作元素的 className dom 属性?
    • 我迷路了
    • 没有引用重写?
    • 如果我们已安装,则从渲染中的 dom 读取,否则假定有效?
  • 状态:
    • 删除 if 语句
    • 根据 this.state 进行渲染验证

我们需要将控制权交还给父级。数据现在在 props 中,我们需要对变化做出反应。

  • 参考:
    • 实现 componentDidMount、componentWillUpdate 和 componentDidUpdate
    • 手动区分之前的props
    • 用最少的更改来操作 dom
    • 嘿!我们正在 react 中实现 react...
    • 还有更多,但我的手指受伤了
  • 状态:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

人们认为 refs 比保持状态“更容易”。前 20 分钟可能是这样,但在我之后的经验中,情况并非如此。让你自己说“是的,我会在 5 分钟内完成”,而不是“当然,我会重写一些组件”。

【讨论】:

  • 你能解释一下 sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js 吗?
  • 不,我的意思是对 dom 的实际更改。 React.findDOMNode(this.refs.foo)。如果你例如更改this.refs.foo.props.bar 不会发生任何事情。
  • 例如如果您有 &lt;input onChange={this.handleChange} value={this.state.foo} /&gt; 将其更改为 &lt;input onChange={this.props.handleChange} value={this.props.foo} /&gt;,或修改您的 handleChange 函数以调用 props 中的回调。无论哪种方式,这都是一些明显的小变化。
  • 不确定我是不是唯一一个觉得你的答案有点混乱的人。您能否展示一些代码示例,让您的观点更清楚?
  • 在屏幕上有 50 多个输入,并且在任何状态更改时都进行渲染是不可取的。组件化每个维护自己状态的input 字段是理想的。在某些时候,我们确实需要用一些更大的模型来协调这些不同的独立状态。也许我们在计时器上有一个自动保存,或者我们只是保存在componentWillUnmount 这是我发现refs 理想的地方,在协调过程中,我们从每个ref 中提取state 值,没有一个是更明智的。我同意在大多数情况下state 是答案,但是对于大量的inputs,使用适当的refs 模式是一个性能提升
【解决方案2】:

我看到一些人引用上述答案作为“从不使用 refs”的理由,我想给出我(以及我与之交谈过的其他一些 React 开发人员)的意见。

在谈论将它们用于组件实例时,“不要使用 refs”的观点是正确的。这意味着,您不应该使用 refs 作为获取组件实例并在其上调用方法的一种方式。这是使用 refs 的不正确方式,并且当 refs 快速向南时。

使用 refs 的正确(并且非常有用)的方法是当您使用它们从 DOM 中获取一些值时。例如,如果您有一个输入字段将 ref 附加到该输入,那么稍后通过 ref 获取值就可以了。如果没有这种方式,您需要经过一个相当协调的过程,以使您的输入字段与您当地的州或助焊剂商店保持同步——这似乎没有必要。

2019 年编辑:未来的朋友你好。除了我几年前提到的 ^ 之外,使用 React Hooks,refs 也是在渲染之间跟踪数据的好方法,并且不仅限于抓取 DOM 节点。

【讨论】:

  • 你的最后一段很有道理,但你能澄清一下你的第二段吗?获取组件实例并调用被认为不正确的方法的具体示例是什么?
  • 我同意这一点。我使用 refs 除非/直到我需要对字段的值进行验证或操作。如果我确实需要验证更改或以编程方式更改值,那么我使用状态。
  • 我也同意这一点。在发现阶段,我故意天真地接近了一个包含大量输入的屏幕。存储在由 id 键入的映射(处于状态)中的所有输入值。不用说,由于设置状态和渲染 50 多个输入(一些 material-ui,它们很重!)在这样的微小 UI 更改上(如复选框单击并不理想),因此性能受到了影响。将每个可以保持自己状态的输入组件化似乎是正确的方法。如果需要协调,只需查看refs 并获取状态值。实际上,这似乎是一个非常好的模式。
  • 我完全同意。在我看来,接受的答案太模糊了。
  • 我同意。在设计通用表单组件时,这暴露了受控组件和管理焦点、错误处理等的痛点。实际上不可能有一个干净的架构。如果需要,请与我交谈。我正在将我的组件移动到 refs。
【解决方案3】:

这篇文章很旧。

我将在这件事上分享我对一个案例的小经验。

我正在研究一个包含大量“动态”输入和大量缓存数据的大型组件(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)}

渲染非常繁重,以至于输入变化断断续续,就像****一样(不要试图按住键,文本只会在暂停后出现)

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

结果是这样的:

  const inputsRef = useRef([])

在输入中:

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

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

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

]

多亏了这一点,没有重新渲染,输入流畅,功能以同样的方式工作。它将节省周期和计算,也可以节省能源。为地球做这件事x)

我的结论:甚至可能需要用于输入值的 useRef。

【讨论】:

    【解决方案4】:

    TL;DR 一般来说,refs 与 React 的 declarative philosophy 相悖,所以你应该将它们作为最后的手段。尽可能使用state / props


    要了解refsstate / props 的用途,让我们看看 React 遵循的一些设计原则。

    每个反应documentation 关于refs

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

    Per React 关于Escape Hatches的设计原则

    如果某些对构建应用程序有用的模式难以以声明性方式表达,我们将为它提供命令式 API。 (他们在这里链接到参考)

    这意味着 React 的团队建议避免使用 refs 并将 state / props 用于任何可以以反应/声明方式完成的事情。

    @Tyler McGinnis 提供了一个非常好的answer,并指出

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

    虽然您可以这样做,但您将违背 React 的理念。如果你的输入有价值,它肯定来自state / props。为了保持代码的一致性和可预测性,您也应该坚持使用state / props。我承认refs 有时会为您提供更快的解决方案这一事实,因此如果您进行概念验证,又快又脏 是可以接受的。

    这给我们留下了几个concrete use cases 用于refs

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-04
      • 1970-01-01
      • 2016-02-09
      • 2022-01-19
      • 2017-07-29
      • 2015-12-01
      • 1970-01-01
      相关资源
      最近更新 更多