簡體   English   中英

如何使用 ReactJS 獲取輸入字段的值?

[英]How to get the value of an input field using ReactJS?

我有以下反應組件:

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的 - 任何想法這段代碼有什么問題?

這里有三個答案,取決於你(被迫)使用的 React 版本,以及你是否想使用鈎子。

第一件事:

了解 React 的工作原理很重要,這樣你才能正確地做事(提示:非常值得通過 React 網站上的React 教程運行。它寫得很好,並以一種實際解釋如何做事的方式涵蓋了所有基礎知識) . 這里的“正確”意味着您不是在編寫網頁,而是為碰巧在瀏覽器中呈現的應用程序編寫用戶界面; 所有實際的用戶界面工作都發生在 React 中,而不是“你習慣於編寫網頁”(這就是 React 應用程序真正是“應用程序”而不是“網頁”的原因)。

React 應用程序的渲染基於兩件事:

  1. 由創建該組件實例的父組件聲明的組件屬性,父組件可以在其整個生命周期內對其進行修改,以及
  2. 組件自己的內部狀態,它可以在自己的整個生命周期中對其自身進行修改。

當你使用 React 時,你明確沒有做的是生成 HTML 元素然后使用這些元素:例如,當你告訴 React 使用<input>時,你不是在創建 HTML 輸入元素,而是告訴 React 創建一個 React 輸入對象,當您為 Web 編譯 React 應用程序時,它恰好呈現為 HTML 輸入元素,事件處理由 React 控制。

使用 React 時,您所做的是生成應用程序 UI 元素,向用戶呈現(通常是可操作的)數據,用戶交互以定義的方式更改應用程序的狀態 - 用戶執行的操作可能會更新組件的props 或 state,React 使用它作為信號來為更改的組件生成新的 UI 表示,這可能會導致應用程序界面的一部分更新以反映新狀態。

在此編程模型中,應用程序的內部狀態是最終權限,而不是“用戶查看並與之交互的 UI”:如果用戶嘗試在輸入字段中鍵入內容,而您沒有編寫任何內容來處理它,什么都不會發生:UI 是應用程序狀態的反映,而不是相反。 實際上,瀏覽器 DOM 在這個編程模型中幾乎是一個事后的想法:它恰好是一個超級方便的 UI 框架,幾乎可以保證整個星球都可以訪問(但它不是 React 知道如何使用的唯一一個)

一個具體的例子

有了這些,讓我們看看用戶與輸入元素交互在 React 中是如何工作的。 首先,我們需要有一個 UI 元素供用戶交互:

  1. 您編寫了一個組件來為您的用戶管理(即存儲和呈現)一些字符串數據,並使用onChange函數來處理用戶數據。
  2. React 使用你的組件的渲染代碼生成一個包含input組件(不是 DOM <input>元素)的虛擬 DOM,並將你的onChange處理程序綁定到該組件,以便可以使用 React 事件數據調用它(請注意這不是 DOM change事件偵聽器,並且不會獲得與常規 DOM 事件偵聽器相同的事件數據)。
  3. 然后,React 庫將該虛擬 DOM 轉換為用戶可以與之交互的 UI,並且它將隨着應用程序狀態的變化而更新。 由於它在瀏覽器中運行,因此它構建了一個 HTML 輸入元素。

然后,您的用戶嘗試與該輸入元素進行實際交互:

  1. 您的用戶單擊輸入元素並開始輸入。
  2. 輸入元素還沒有發生任何事情。 相反,輸入事件被 React 攔截並立即終止
  3. React 將瀏覽器事件轉化為 React 事件,並使用 React 事件數據為虛擬 DOM 組件調用onChange函數。
  4. 該函數可能會根據您編寫它的方式執行某些操作,在這種情況下,您幾乎可以肯定編寫它是為了使用用戶(嘗試)鍵入的內容來更新組件的狀態。
  5. 如果安排了狀態更新,React 將在不久的將來運行該狀態更新,這將在更新后觸發渲染通道。
  6. 在渲染過程中,它會檢查狀態是否真的不同,如果是,它會生成一個臨時的第二個虛擬 DOM,它與應用程序的虛擬 DOM(的一部分)進行比較,確定哪一組 add/update/刪除它需要對應用程序的虛擬 DOM 執行的操作,使其看起來與新的臨時 DOM 相同,然后應用這些操作並再次丟棄臨時虛擬 DOM。
  7. 然后它會更新 UI 以反映虛擬 DOM 現在的樣子。
  8. 在所有這些之后,我們終於在用戶實際查看的頁面上獲得了更新的 DOM,他們可以看到他們在輸入元素中輸入的內容。

所以這與常規瀏覽器模型完全不同:不是用戶首先通過在文本框中鍵入來更新 UI 數據,然后我們的代碼讀取“該文本框的當前值”來確定第二個狀態是什么,React 已經知道狀態是什么,並使用事件首先更新狀態,這導致 UI 更新其次

重要的是要記住,所有這一切都會立即有效地發生,所以對於您的用戶來說,他們看起來就像他們在輸入元素中輸入文本的方式與輸入任何隨機網頁的方式一樣,但在幕后,事情再多不過了不同,但仍然導致相同的結果。

所以,有了這些,讓我們看看如何從 React 中的元素中獲取值:

組件類和 ES6(React 16+ 和 15.5 過渡)

從 React 16(以及從 15.5 開始軟啟動)開始不再支持createClass調用,需要使用類語法。 這改變了兩件事:明顯的類語法,以及createClass可以“免費”執行的this上下文綁定,因此為了確保一切仍然有效,請確保您在this上下文中使用“胖箭頭”表示法,在onWhatever中保留匿名函數處理程序,例如我們在此處代碼中使用的onChange

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.reset();
  }

  reset() {
    // Always set the initial state in its own function, so that
    // you can trivially reset your components at any point.
    this.state = {
      inputValue: ''
    };
  }

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

  updateInputValue(evt) {
    const val = evt.target.value;
    // ...       
    this.setState({
      inputValue: val
    });
  }
});

您可能還看到人們在其構造函數中使用bind來處理所有事件處理函數,如下所示:

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

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

不要那樣做。

幾乎在您使用bind的任何時候,諺語“您做錯了”都適用。 您的類已經定義了對象原型,因此已經定義了實例上下文。 不要把bind放在上面; 使用正常的事件轉發而不是在構造函數中復制所有函數調用,因為這種復制會增加您的錯誤表面,並且使跟蹤錯誤變得更加困難,因為問題可能出在您的構造函數中,而不是您調用代碼的位置。

“但它會不斷地在重新渲染中創建和丟棄函數!” 這可能是真的,但你不會注意到。 您的用戶也不是。 如果事件處理程序垃圾收集是您的性能瓶頸,那么已經出現了很多問題,您需要停下來重新考慮您的設計:React 工作得如此出色的原因是因為它不會更新整個 UI,它只會更新發生變化的部分,並且在一個設計良好的 UI 中,大部分 UI 不發生變化所花費的時間超過了 UI 的一小部分花費在更新上的時間。

帶有鈎子的函數組件(React 16.8+)

從 React 16.8 開始,函數組件(即,實際上只是一個將一些props作為參數的函數,可以像它是組件類的實例一樣使用,而無需編寫類)也可以通過使用hooks來賦予狀態。

如果您不需要完整的類代碼,並且單個實例函數就可以了,那么您現在可以使用useState鈎子為自己獲取單個狀態變量及其更新函數,其工作原理與上述示例大致相同,除了沒有“通用” setState函數調用,並為您正在使用的每個值使用一個專用狀態設置器:

import { useId, useState } from 'react';

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

以前類和函數組件之間的非官方區別是“函數組件沒有狀態”,所以我們不能再把它隱藏起來了:函數組件和類組件之間的區別可以在很好的頁面中找到。 - 編寫的反應文檔(沒有捷徑的一條線解釋可以方便地為您誤解!)您應該閱讀這些文檔,以便您知道自己在做什么,從而可以知道您是否選擇了最好的(無論這對您意味着什么)解決方案來自己編程出於您遇到的問題。

React 15 及更低版本,使用舊版 ES5 和createClass

為了正確地做事,你的組件有一個狀態值,它通過輸入字段顯示,我們可以通過讓 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來調度狀態更新,而render this.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']}/>
    });
  }
});

通過執行以下操作設法獲取輸入字段值:

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

您應該使用 MyComponent 類下的構造函數 extends React.Component

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

然后你會得到title的結果

在反應 16 中,我使用

<Input id="number" 
       type="time" 
       onChange={(evt) => { console.log(evt.target.value); }} />

<input>一個唯一的 id

<input id='title' ...>

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

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

無需在每次按鍵時不斷更新 React 狀態。 只需在需要時獲取值。

export default class App extends React.Component{
     state={
         value:'',
         show:''
      }

handleChange=(e)=>{
  this.setState({value:e.target.value})
}

submit=()=>{
   this.setState({show:this.state.value})
}

render(){
    return(
        <>
          <form onSubmit={this.submit}>
             <input type="text" value={this.state.value} onChange={this.handleChange} />
             <input type="submit" />
          </form>
          <h2>{this.state.show}</h2>
        </>
        )
    }
}

在功能組件中:-

export default function App(){

const [state, setState] = useState({
        value:'',
        show:''
    });

const handleChange = (e) => {
    setState({value: e.target.value})
}

const submit = () => {
    setState({show: state.value})
}

return(
        <>
            <form onSubmit={()=>submit()}>
                <input type="text" value={state.value} onChange={(e)=>handleChange(e)} />
                <input type="submit" />
            </form>
            <h2>{state.show}</h2>
        </>
)}

我通過將其綁定到函數updateInputValue(evt)成功地做到了這this

this.updateInputValue = this.updateInputValue.bind(this);

然而 input value={this.state.inputValue} ... 原來不是一個好主意。

這是 babel ES6 中的完整代碼:

class InputField extends React.Component{

    
  constructor(props){
   super(props);
   //this.state={inputfield: "no value"};   
   this.handleClick = this.handleClick.bind(this);
   this.updateInputValue = this.updateInputValue.bind(this);
  }
  
  handleClick(){
   console.log("trying to add picture url");
   console.log("value of input field : "+this.state.inputfield);
   
  }
 
  updateInputValue(evt){
    //console.log("input field updated with "+evt.target.value);
    this.state={inputfield: evt.target.value};   
    
  }

  render(){
    var r; 
    r=<div><input type="text" id="addpixinputfield" 
            onChange={this.updateInputValue} />
      <input type="button" value="add" id="addpix" onClick={this.handleClick}/>
      </div>;    
    return r;
   }
}

在函數組件中

使用狀態

返回一個有狀態的值,以及一個更新它的函數。 在初始渲染期間,返回的狀態(state)與作為第一個參數(initialState)傳遞的值相同。 setState函數用於更新狀態。 它接受一個新的狀態值並將組件的重新渲染排入隊列。
src ---> https://reactjs.org/docs/hooks-reference.html#usestate

使用參考

useRef返回一個可變的 ref 對象,其.current屬性初始化為傳遞的參數(initialValue) 返回的對象將在組件的整個生命周期內持續存在。
src ---> https://reactjs.org/docs/hooks-reference.html#useref

import { useRef, useState } from "react";

export default function App() {
  const [val, setVal] = useState('');
  const inputRef = useRef();

  const submitHandler = (e) => {
    e.preventDefault();

    setVal(inputRef.current.value);
  }

  return (
    <div className="App">
      <form onSubmit={submitHandler}>
        <input ref={inputRef} />
        <button type="submit">Submit</button>
      </form>

      <p>Submit Value: <b>{val}</b></p>
    </div>
  );
}

你的錯誤是因為你使用類,當使用類時,我們需要將函數與 this 綁定才能正常工作。 無論如何,有很多教程為什么我們應該“this”以及“this”在javascript中做什么。

如果您更正提交按鈕,它應該可以工作:

<button type="button" onClick={this.onSubmit.bind(this)} className="btn">Save</button>

而且,如果您想在控制台中顯示該輸入的值,您應該使用 var title = this.title.value;

  • 反應版本:17.0.1

    a) 使用功能組件

    b) 使用鈎子管理狀態:useState()。

如上編寫和運行代碼:

import React, {useState} from 'react';

const InputElement = () => {
  const [inputText, setInputText] = useState('');

  return (
   <div> 
        <input
              onChange={(e) => {
                  setInputText(e.target.value);
                   }                   
             }
             placeholder='Enter Text'
       />
       {inputText}
   </div>
 );
}

求解方案算法類似於雙向數據綁定:

輸入 <=> DATA_MODEL <=> Label_Text

// On the state
constructor() {
  this.state = {
   email: ''
 }
}

// Input view ( always check if property is available in state {this.state.email ? this.state.email : ''}

<Input 
  value={this.state.email ? this.state.email : ''} 
  onChange={event => this.setState({ email: event.target.value)}
  type="text" 
  name="emailAddress" 
  placeholder="johdoe@somewhere.com" />

這種最簡單的方法是使用箭頭函數

帶有箭頭功能的代碼

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

};

您可以在不添加“onChange”函數的情況下獲取輸入值。

只需在輸入元素中添加一個 'ref attr:

然后在需要時使用 this.refs 獲取輸入值。

將您的 ref 更改為: ref='title'並刪除name='title'然后刪除var title = this.title並寫入:

console.log(this.refs.title.value)

您還應該將.bind(this)添加到this.onSubmit

(它在我的情況下非常相似,但不是onClick我有onSubmit={...}並且它被放入表單( <form onSubmit={...} ></form>)

如果您使用類組件,那么只需 3 個步驟 - 首先您需要為輸入字段聲明狀態,例如this.state = {name:''} 其次,您需要編寫一個函數來設置在下面的示例中更改狀態時的狀態,它是setName() ,最后您必須編寫輸入 jsx,例如< input value={this.name} onChange = {this.setName} />

import React, { Component } from 'react'

export class InputComponents extends Component {
    constructor(props) {
        super(props)

        this.state = {
             name:'',
             agree:false
        }
        this.setName = this.setName.bind(this);
        this.setAgree=this.setAgree.bind(this);
    }

    setName(e){
        e.preventDefault();
        console.log(e.target.value);
        this.setState({
            name:e.target.value
        })
    }
    setAgree(){
        this.setState({
            agree: !this.state.agree
        }, function (){
            console.log(this.state.agree);
        })
    }
    render() {
        return (
            <div>
                <input type="checkbox" checked={this.state.agree} onChange={this.setAgree}></input>
                < input value={this.state.name} onChange = {this.setName}/>
            </div>
        )
    }
}

export default InputComponents
export default class MyComponent extends React.Component {

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

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

};

使用不受控制的字段:

export default class MyComponent extends React.Component {

    onSubmit(e) {
        e.preventDefault();
        console.log(e.target.neededField.value);
    }

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

};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM