在 React.js 表单组件中使用 state 或 refs?

IT技术 reactjs
2021-04-10 00:29:50

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

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

4个回答

简短版本:避免引用。


它们不利于可维护性,并且失去了 WYSIWYG 模型渲染提供的很多简单性。

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

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

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

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

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

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

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

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

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

例如,如果您已将其 <input onChange={this.handleChange} value={this.state.foo} />更改为<input onChange={this.props.handleChange} value={this.props.foo} />,或修改您的 handleChange 函数以调用 props 中的回调。无论哪种方式,它都是一些小的明显变化。
2021-05-22 00:29:50
不,我的意思是对 dom 的实际更改。 React.findDOMNode(this.refs.foo). 如果你例如改变this.refs.foo.props.bar什么都不会发生。
2021-05-25 00:29:50
你能解释一下 sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js 吗?
2021-06-02 00:29:50
在屏幕上有 50 多个输入,并且在任何状态更改时渲染每个输入都是不可取的。将每个input字段组件化,每个字段都保持自己的状态是理想的。在某些时候,我们确实需要用一些更大的模型来协调这些不同的独立状态。也许我们在计时器上有一个自动保存,或者我们只是保存在componentWillUnmount这是我认为refs理想的地方,在对帐期间,我们state从每个 中提取值ref,没有一个是更明智的。我同意在大多数情况下state是答案,但是对于大量的inputs,使用适当的refs模式是性能的福音
2021-06-14 00:29:50
不确定我是不是唯一一个觉得你的答案有点混乱的人。你能展示一些代码示例让你的观点更清晰吗?
2021-06-21 00:29:50

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

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

使用 refs 的正确(且非常有用)的方法是使用它们从 DOM 中获取一些值。例如,如果您有一个输入字段将 ref 附加到该输入,那么稍后通过 ref 获取值就可以了。如果没有这种方式,您需要通过一个相当精心策划的过程来使您的输入字段与您的本地状态或您的通量存储保持同步——这似乎是不必要的。

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

我也同意这一点。在发现阶段,我特意用 naivete 接近了一个有大量输入的屏幕。存储在由 id 键控的映射(处于状态)中的所有输入值。毋庸置疑,由于设置状态和渲染 50 多个输入(一些材质 ui,它们很重!)在诸如复选框单击之类的微小 UI 更改上并不理想,因此性能受到了影响。将可以保持其自身状态的每个输入组件化似乎是正确的方法。如果需要和解,只需查看refs并获取状态值。实际上,这似乎是一个非常好的模式。
2021-05-22 00:29:50
我完全同意。在我看来,接受的答案太模糊了。
2021-05-27 00:29:50
我同意这一点。我使用 refs 除非/直到我需要验证或操作字段的值。如果我确实需要以编程方式验证更改或更改值,那么我使用 state。
2021-06-02 00:29:50
我同意。在设计一个通用的 Form 组件时,这揭示了受控组件和管理焦点、错误处理等的痛点。实际上不可能有一个干净的架构。如果需要,请与我交谈。我正在将我的组件移至 refs。
2021-06-11 00:29:50
你的最后一段很有意义,但你能澄清你的第二段吗?获取组件实例并调用被认为不正确的方法的具体示例是什么?
2021-06-20 00:29:50

这个帖子很旧了。

我就这方面的一个案例分享我的一点经验。

我正在研究一个包含大量“动态”输入和大量缓存数据的大型组件(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 甚至可能是需要的。

TL;DR一般来说,refs违背 React 的声明式哲学,所以你应该将它们作为最后的手段。state / props尽可能使用


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

每个 React文档关于refs

避免对任何可以声明性完成的事情使用 refs。

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

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

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

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

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

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

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

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