如何使用 ReactJS 获取输入字段的值?

IT技术 javascript reactjs
2021-02-21 03:40:27

我有以下 React 组件:

export default class MyComponent extends React.Component {

    onSubmit(e) {
        e.preventDefault();
        var title = this.title;
        console.log(title);
    }

    render(){
        return (
            ...
            <form className="form-horizontal">
                ...
                <input type="text" className="form-control" ref={(c) => this.title = c} name="title" />
                ...
            </form>
            ...
            <button type="button" onClick={this.onSubmit} className="btn">Save</button>
            ...
        );
    }

};

控制台给了我undefined- 知道这段代码有什么问题吗?

6个回答

这里有三个答案,这取决于你(被迫)使用的 React 版本,以及你是否想要使用钩子。

第一件事:

了解 React 的工作原理很重要,这样你才能正确地做事(提示:通过 React 网站上的 React 教程练习非常值得的。它写得很好,并以一种实际解释如何做事的方式涵盖了所有基础知识)。这里的“正确”意味着您正在编写一个恰好在浏览器中呈现的应用程序界面;所有的界面工作都发生在 React 中,而不是“你在写网页时习惯的东西”(这就是为什么 React 应用程序是“应用程序”,而不是“网页”)。

React 应用程序的呈现基于两件事:

  1. 由任何父级声明的组件属性创建该组件的实例,父级可以在其整个生命周期中修改该实例,以及
  2. 组件自己的内部状态,它可以在整个生命周期中修改自己。

当您使用 React 时,您明确没有做的是生成 HTML 元素然后使用这些元素:<input>例如,当您告诉 React 使用 an 时,您不是在创建 HTML 输入元素,而是在告诉 React 创建一个 React 输入对象恰好呈现为 HTML 输入元素,其事件处理查看HTML 元素的输入事件但不受 控制

使用 React 时,您所做的是生成应用程序 UI 元素,向用户呈现(通常是可操作的)数据,用户交互会改变组件的状态,这可能会导致应用程序界面的一部分重新呈现以反映新状态。在这个模型中,状态始终是最终的权威,而不是“使用任何 UI 库来渲染它”,在网络上它是浏览器的 DOM。在这个编程模型中,DOM 几乎是事后的想法:它只是 React 碰巧使用的特定 UI 框架。

所以在输入元素的情况下,逻辑是:

  1. 你输入输入元素,
  2. 您的输入元素还没有发生任何事情,该事件被 React 拦截并立即终止
  3. React 将事件转发到您为事件处理设置的函数,
  4. 该函数可以安排状态更新,
  5. 如果是,React 运行该状态更新(异步!)并会render在更新后触发调用,但前提是状态更新更改了状态。
  6. 只有此渲染发生后,UI 才会显示您“键入了一个字母”。

所有这一切都在几毫秒内发生,如果不是更短的话,所以看起来您以与“仅在页面上使用输入元素”相同的方式输入输入元素,但这绝对不是什么发生了。

所以,话虽如此,关于如何从 React 中的元素获取值:

React 15 及以下,使用 ES5

为了正确地做事,你的组件有一个状态值,它通过输入字段显示,我们可以通过让 UI 元素将更改事件发送回组件来更新它:

var Component = React.createClass({
  getInitialState: function() {
    return {
      inputValue: ''
    };
  },

  render: function() {
    return (
      //...
      <input value={this.state.inputValue} onChange={this.updateInputValue}/>
      //...
    );
  },

  updateInputValue: function(evt) {
    this.setState({
      inputValue: evt.target.value
    });
  }
});

所以我们告诉 React 使用该updateInputValue函数来处理用户交互,使用它setState来安排状态更新,事实上,rendertap intothis.state.inputValue意味着当它在更新状态后重新渲染时,用户将根据他们输入的内容看到更新文本。

基于评论的附录

鉴于 UI 输入表示状态值(考虑如果用户在中途关闭他们的选项卡,并且选项卡被恢复会发生什么。他们填写的所有这些值是否应该被恢复?如果是,那就是状态)。这可能会让你觉得一个大表单需要几十个甚至一百个输入表单,但 React 是以可维护的方式建模你的 UI:你没有 100 个独立的输入字段,你有一组相关的输入,所以你捕获每个在一个组件中分组,然后将您的“主”表单构建为一组组。

MyForm:
  render:
    <PersonalData/>
    <AppPreferences/>
    <ThirdParty/>
     ...

这也比一个巨大的单一表单组件更容易维护。将组拆分为具有状态维护的组件,其中每个组件一次仅负责跟踪几个输入字段。

您可能还会觉得写出所有这些代码“很麻烦”,但这是一种错误的节省:不是您的开发人员,包括未来的您,实际上从看到所有这些输入明确连接起来后受益匪浅,因为它使代码路径更容易跟踪。但是,您始终可以优化。例如,您可以编写一个状态链接器

MyComponent = React.createClass({
  getInitialState() {
    return {
      firstName: this.props.firstName || "",
      lastName: this.props.lastName || "" 
      ...: ...
      ...
    }
  },
  componentWillMount() {
    Object.keys(this.state).forEach(n => {
      let fn = n + 'Changed';
      this[fn] = evt => {
        let update = {};
        update[n] = evt.target.value;
        this.setState(update);
      });
    });
  },
  render: function() {
    return Object.keys(this.state).map(n => {
      <input
        key={n} 
        type="text"
        value={this.state[n]}
        onChange={this[n + 'Changed']}/>
    });
  }
});

当然,还有改进的版本,因此请访问 https://npmjs.com并搜索您最喜欢的 React 状态链接解决方案。开源主要是寻找其他人已经完成的工作,并使用它而不是自己从头开始编写所有内容。

React 16(和 15.5 过渡版)和“现代”JS

从 React 16(和 15.5 软启动)开始,createClass不再支持调用,需要使用类语法。这改变了两件事:明显的类语法,还有可以“免费”执行this上下文绑定createClass,因此为确保事情仍然有效,请确保您使用“粗箭头”表示法thisonWhatever处理程序中保留匿名函数的上下文,例如在onChange这里的代码中,我们使用:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: ''
    };
  }

  render() {
    return (
      //...
      <input value={this.state.inputValue} onChange={evt => this.updateInputValue(evt)}/>
      //...
    );
  },

  updateInputValue(evt) {
    this.setState({
      inputValue: evt.target.value
    });
  }
});

您可能还看到人们bind在其构造函数中使用所有事件处理函数,如下所示:

constructor(props) {
  super(props);
  this.handler = this.handler.bind(this);
  ...
}

render() {
  return (
    ...
    <element onclick={this.handler}/>
    ...
  );
}

不要那样做。

几乎在您使用 的任何时候bind,谚语“您做错了”都适用。您的类已经定义了对象原型,因此已经定义了实例上下文。不要把bind它放在首位;使用正常的事件转发,而不是在构造函数中复制所有函数调用,因为这种重复会增加您的错误面,并使跟踪错误变得更加困难,因为问题可能出在您的构造函数中,而不是您调用代码的位置。以及将维护负担置于您(必须或选择)与之合作的其他人身上。

是的,我知道react文档说这很好。不是,不要这样做。

React 16.8,使用带有钩子的函数组件

从 React 16.8 开始,函数组件(即实际上只是一个将 someprops作为参数的函数可以用作组件类的实例,而无需编写类)也可以通过使用hooks来获得状态

如果您不需要完整的类代码,而单个实例函数就可以,那么您现在可以使用useState钩子为自己获取一个状态变量,及其更新函数,其工作原理与上述示例大致相同,只是没有“通用”setState函数调用并为您正在处理的每个值使用一个专用状态设置器:

import { useState } from 'react';

function myFunctionalComponentFunction() {
  const [input, setInput] = useState(''); // '' is the initial state value
  return (
    <div>
    <label>Please specify:</label>
    <input value={input} onInput={e => setInput(e.target.value)}/>
    </div>
  );
}

以前类和函数组件之间的非官方区别是“函数组件没有状态”,所以我们不能再躲在那个后面:函数组件和类组件之间的区别可以在很好的几页中找到- 编写的react文档(没有捷径可以方便地误解你!)你应该阅读它,这样你就知道你在做什么,从而知道你是否选择了最好的(无论对你来说意味着什么)解决方案来为你自己编程出于您遇到的问题。

另请注意,“100 个字段”大多无关紧要:拆分您的表单,因为它不是 100 个元素,它是几个部分,每个部分都有许多输入,因此应用良好的设计并使每个部分成为自己的组件,并为表单分组组件组。这通常使得组件负责少于10个输入,突然你的信息架构使得很多更有意义。表单提交,作为浏览器的一个动作,当然只是看到“你的表单”并一次性提交所有内容。干净、有针对性的 UI 设计。
2021-04-17 03:40:27
@JasonChing 那么您只是在代码中构建了潜在的错误。React 不是网页的“一劳永逸”的解决方案,它是一个构建界面的框架,负责状态管理和实际 UI 渲染作为事后(你的用户不与 DOM 交互,他们与React. DOM 更新只是一个异步(但非常快)的最后一步,以便 UI 在视觉上反映状态)。如果你想绕过它,更重要的问题是:你为什么使用 React?因为你错误使用它的最好标志是结合 React 和 jQuery。
2021-04-30 03:40:27
所以编写更智能的代码。表单输入绝对是状态(考虑如果用户在中途关闭他们的选项卡会发生什么,并且选项卡被恢复。他们填写的所有这些值是否应该被恢复?是的?那是 state),所以写一些状态维护代码。Facebook 也有数百个表单值,他们的疯狂解决方案是 React。真的很好用。在仍然使用状态的同时,使您的代码更简单的一种方法是使用双向状态链接,同样在 React 站点上进行了解释。值得一读!=)
2021-05-04 03:40:27
感谢您的评论。然而,我确实注意到状态链接已从 React v15 开始被弃用。
2021-05-04 03:40:27
我读过一些在线文章,这些文章说使用过多的状态是一个坏主意。在我的应用程序的一种特定形式中,我有大约 100 个表单字段。定义一个函数来保存状态感觉像是一种不必要的艰巨的做事方式。如果我可以使用 onClick={this.onSubmit.bind(this)},这似乎是获取值的好方法(然后,如果我愿意,可以设置组件状态)- 我希望对此有一些评论。
2021-05-12 03:40:27

通过执行以下操作设法获取输入字段值:

import React, { Component } from 'react';

class App extends Component {

constructor(props){
super(props);

this.state = {
  username : ''
}

this.updateInput = this.updateInput.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}


updateInput(event){
this.setState({username : event.target.value})
}


handleSubmit(){
console.log('Your input value is: ' + this.state.username)
//Send state to the server code
}



render(){
return (
    <div>
    <input type="text" onChange={this.updateInput}></input>
    <input type="submit" onClick={this.handleSubmit} ></input>
    </div>
  );
}
} 

//output
//Your input value is: x
为什么使用“setState”?这涉及重新渲染..不是吗?
2021-04-30 03:40:27

你应该在类 MyComponent extends React.Component 下使用构造函数

constructor(props){
    super(props);
    this.onSubmit = this.onSubmit.bind(this);
  }

然后你会得到标题的结果

在react 16 中,我使用

<Input id="number" 
       type="time" 
       onChange={(evt) => { console.log(evt.target.value); }} />
如果该字段在年龄负载时自动填充,则不起作用
2021-05-11 03:40:27

<input>一个唯一的id

<input id='title' ...>

然后使用标准的 Web API在 DOM 中引用它

const title = document.getElementById('title').value

无需在每次按键时不断更新 React 状态。只需在需要时获取值。