React 綁定事件的四種方式_事件傳參_表單事件_受控組件與非受控組件

1. 簡介

React 元素的事件處理和 DOM 元素類似。但是有一點語法上的不同:

  • React 事件綁定屬性的命名採用駝峯式寫法,而不是小寫,例如原生的事件全是小寫 onclick,React 裏的事件是駝峯 onClick。
  • 如果採用 JSX 的語法需要傳入一個函數作爲事件處理函數,而不是一個字符串(DOM 元素的寫法)。
  • React的事件並不是原生事件,而是合成事件。
  • 在 React 中另一個不同是你不能使用返回 false 的方式阻止默認行爲, 你必須明確的使用 e.preventDefault()。

HTML 通常寫法是:

<button onclick="clickHandle()">提交</button>

<script>
function clickHandle(e){
	e.preventDefault();	//阻止默認事件
	//或者使用如下的寫法
	return false;
}
</script>

React 中寫法爲:

<button onClick={clickHandle}>提交</button>

<script>
function clickHandle(e){
	e.preventDefault();	//阻止默認事件,不能使用return false
}
</script>

2. React 綁定事件的幾種方式

  • 當使用 ES6 class 語法來定義一個組件的時候,事件處理器會成爲類的一個方法。
  • 必須謹慎對待 JSX 回調函數中的 this,類的方法默認是不會綁定 this 的。如果你忘記綁定 this.handleClick 並把它傳入 onClick, 當你調用這個函數的時候 this 的值會是 undefined。
  • 通常情況下,如果你沒有在方法後面添加 () ,例如 onClick={this.handleClick},你應該爲這個方法綁定 this。

2.1 方式一

在回調函數中使用箭頭函數,直接在 render() 裏寫行內的箭頭函數(不推薦),這種寫法存在的問題就是,當每次執行render() 的時候都會創建一個不同的回調函數。在大多數情況下,這沒有問題。然而如果這個回調函數作爲一個屬性值傳入低階組件,這些組件可能會進行額外的重新渲染。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
  handleClick(e,val){
  	console.log(e,val);
  }
  render() {
    return (
      <button onClick={(e)=>this.handleClick(e,'aa')}>添加</button>
    );
  }
}

ReactDOM.render(
  <Box />,
  document.getElementById('root')
);

2.2 方式二

在 render() 方法中,使用 bind 綁定 this(不推薦)。直接在組件內定義一個非箭頭函數的方法,然後在render裏直接使用onClick={this.handleClick.bind(this)},這種方式的缺點是,每次都使用 bind 綁定 this,代碼會冗餘:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
	handleClick(val,e) {	//事件對象e要放在最後
		console.log(val,e);
	}
	render() {
		return (
			<button onClick={this.handleClick.bind(this, 'aa')}>添加</button>
		);
	}
}

ReactDOM.render(
  <Box />,
  document.getElementById('root')
);

2.3 方式三

使用屬性初始化器來正確的綁定回調函數(推薦),在組件內使用箭頭函數定義一個方法,這種方式的缺點是不能自定義傳參:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
	handleClick =(e)=>{
		console.log(e);
	}
	render() {
		return (
			<button onClick={this.handleClick}>添加</button>
		);
	}
}

ReactDOM.render(
  <Box />,
  document.getElementById('root')
);

2.4 方式四

直接在組件內定義一個非箭頭函數的方法,然後在constructor裏bind(this)(推薦),這種方式的優點是性能比較好,不管render()執行多少次,最終都指向同一個引用。這種方式的缺點是不能自定義傳參。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
	constructor(){
		super();
		this.myhandleClick = this.handleClick.bind(this);
	}
	handleClick(e){
		console.log(e);
	}
	render() {
		return (
			<button onClick={this.myhandleClick}>添加</button>
		);
	}
}

ReactDOM.render(
  <Box />,
  document.getElementById('root')
);

3. React 事件傳參

和普通瀏覽器一樣,事件 handleClick 會被自動傳入一個 event 對象,這個對象和普通的瀏覽器 event 對象所包含的方法和屬性都基本一致。不同的是 React中的 event 對象並不是瀏覽器提供的,而是它自己內部所構建的。它同樣具有event.stopPropagationevent.preventDefault 這種常用的方法。

爲事件處理程序傳遞額外的參數,通常使用上面前兩種(2.1、2.2)方式,即:

<button onClick={(e)=>this.handleClick(e, 'aa')}>添加</button>
<button onClick={this.handleClick.bind(this, 'aa')}>添加</button>

值得注意的是,通過 bind 方式向監聽函數傳參,在類組件中定義的監聽函數,事件對象 e 要排在所傳遞參數的後面。

3.1 子組件中修改參數

在子組件中修改父組件傳過來的參數,比較推薦的方法是, 在父組件中定義方法,子組件中調用父組件的方法,通過props傳遞到子組件中,然後在子組件中通過this.props.method來調用。看下面的例子:

子組件中代碼:

class Button extends Component {
  handleClick(){
  	//執行DOM元素的 change屬性
    this.props.change();
  }
  render() {
    return (
     <button onClick={()=>this.handleClick()}>
       {this.props.children}
     </button>
    );
  }
}

父組件中代碼:

class Counter extends Component {
  constructor() {
    super();
    this.state = {
      count: 0
    }
  }
  handleChange(type) {
    this.setState((preState, props) => {
          count: preState.count + 1
      } 
    }, () => { })
  }

  render() {
    return (
      <div >
        <Button change={() => this.handleChange()}>-</Button>
      </div>
    );
  }
}

4. 表單事件

HTML 表單元素與 React 中的其他 DOM 元素有所不同,因爲表單元素生來就保留一些內部狀態。

在 HTML 當中,像 <input>, <textarea>, 和 <select> 這類表單元素會維持自身狀態,並根據用戶輸入進行更新。但在React中,可變的狀態通常保存在組件的狀態屬性中,並且只能用 setState() 方法進行更新。

4.1 受控組件

對於受控組件來說,輸入的值始終由 React 的 state 驅動。渲染表單的 React 組件還控制着用戶輸入過程中表單發生的操作。被 React 以這種方式控制取值的表單輸入元素就叫做“受控組件”。

例如,如果想讓用戶輸入的小寫字母全部轉成大寫字母,我們可以將表單寫爲受控組件:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Form extends Component {
  constructor() {
    super();
    this.state = {name: ""}
  }
  handleChange(event) {
    this.setState({
      name: event.target.value.toUpperCase() //	將輸入的字母轉成大寫
    })
  }
  render() {
    return (
      <div>名稱:<input type="text" value={this.state.name} onChange={this.handleChange.bind(this)} /></div>
    );
  }
}

ReactDOM.render(
  <Form />,
  document.getElementById('root')
);

由於在表單元素上設置了 value 屬性,因此顯示的值將始終爲 this.state.value,這使得 React 的 state 成爲唯一數據源。使用 onChange 事件來監聽 input 的變化,並修改 state。由於 handlechange 在每次按鍵時都會執行並更新 React 的 state,將字母轉成大寫,因此顯示的值將隨着用戶輸入而更新。

4.2 多個 input 元素

  • 當有多個 input 元素需要處理時,你可以通過給每個元素添加一個 name 屬性,來讓處理函數根據 event.target.name的值來選擇做什麼。
  • 在 React 中,<textarea> 使用 value 屬性代替。這樣,可以使得使用 <textarea> 的表單和使用單行 input 的表單非常類似。
  • 在 React 中,Select 下拉菜單,不使用 selected 屬性,而在根 select 標籤上用 value 屬性來表示選中項,當 Select 爲多選時,對應的 state 應該爲一個數組。
  • 注意:<input type=“file”> 的 value 只讀,所以它是 React 中的一個非受控組件。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Form extends Component {
  constructor() {
    super();
    this.state = {
      name: "",
      desc: "",
      city: [1,2]
    }
  }
  handleChange(event) {
    if(event.target.name==='city'){
      this.state.city.push(event.target.value);
      this.setState({})
      return;
    }
    this.setState({
      [event.target.name]: event.target.value
    })
  }
  render() {
    return (
      <div>
        名稱:<input type="text" value={this.state.name} name="name" onChange={this.handleChange.bind(this)} />
        描述:<textarea name="desc" value={this.state.desc} onChange={this.handleChange.bind(this)}></textarea>
        城市:<select name="city" multiple 
        value={this.state.city}
        onChange={this.handleChange.bind(this)}>
          <option value="1">北京</option>
          <option value="2">上海</option>
          <option value="3">天津</option>
        </select>
      </div>
    );
  }
}

ReactDOM.render(
  <Form />,
  document.getElementById('root')
);

總的來說,這使得 <input type=“text”>, <textarea> 和 <select> 之類的標籤都非常相似—它們都接受一個 value 屬性,你可以使用它來實現受控組件。

4.3 受控組件應用場景

受控組件的應用場景:可以監聽用戶輸入,改變用戶輸入的數據。例如上面將輸入的字母轉成大寫、判斷用戶是否輸入爲空等。

如果你想尋找包含驗證、追蹤訪問字段以及處理表單提交的完整解決方案,使用 Formik 是不錯的選擇。然而,它也是建立在受控組件和管理 state 的基礎之上

有時使用受控組件會很麻煩,因爲你需要爲數據變化的每種方式都編寫事件處理函數,並通過一個 React 組件傳遞所有的輸入 state。在這些情況下,你可能希望使用非受控組件, 這是實現輸入表單的另一種方式。

4.4 非受控組件

非受控組件,組件展示的值,完全不受 state 的控制。 使用 ref 屬性,用來綁定到 render() 輸出的任何組件上。通過 ref 屬性獲取綁定的 DOM 元素,跟 vue 中 ref 的使用類似。

ref 使用方法:

當 ref 屬性值爲靜態內容時:
1、綁定一個 ref 屬性到 render 的返回值上;
2、通過 this.refs 獲取綁定的 DOM 元素;

當 ref 屬性值爲 state 時:
1、使用 import 導入 react.createRef ;
2、將 state 作爲 ref 的屬性值,綁定到 render 的返回值上;
3、通過 state.current 獲取綁定的 DOM 元素;

import React, { Component,createRef } from 'react';
import ReactDOM from 'react-dom';

class Form extends Component {
  constructor() {
    super();
    this.title = createRef()
  }
  handleSubmit = () => {
    console.log(this.title.current.value)
    console.log(this.refs.myInput.value)
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.title}/>
        <input type="text" ref='myInput'/>
        <button onClick={this.handleSubmit}>提交</button>
      </div>
    );
  }
}

ReactDOM.render(
  <Form />,
  document.getElementById('root')
);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章