1. 程式人生 > >react整理(二)元件之間的傳值

react整理(二)元件之間的傳值

上一節說到如何在react中建立元件,知道了建立,還得知道怎麼使用,這一節來整理react元件之間的傳值。

react的核心思想就是元件化,元件之間UI的展示主要通過值的傳遞。我們可以將元件的定義看成以下的公式:

UI = Component(props, state)

也就是說,元件是根據props,state來定義介面UI的。props是對外的資料,而state是對內的資料。
props與state的區別在於,props資料是不可變的,而state的資料是可變的。

對於props來說,元件裡可以引用別的元件,元件之間的引用會構成一個狀態樹。那麼,父級元件可以通過子元件的props來傳值,但是這種值的傳遞是單向的,子元件不被允許更改父級元件的值,若要改變,只能更改在父級元件中更改。這裡我們暫且說說,父級元件如何傳值給子元件,至於子元件如何主動去更改父級元件,我們在後續詳細說明。

通過一個例子來了解父級元件如何向子元件傳遞資料:
有兩個元件,一個是父級元件,我們叫它index,一個元件,我們叫Children。父級元件index引用子元件Children,可通過子元件的Props來傳值。

import react, {Component} from 'React';
import Children from 'Children';

export default class Index extends Component{
render(){
	// 向Children傳入值
	const  params = {
		sort: 0,
		name: 'lily'
	}
	return (
		<Children  {...params}/>   // es6解構傳入
	)
  }
}
import react, {Component} from 'React';

export default class Children extends Component{
render(){
	// 通過props獲取到父級傳遞的值
	const { sort, name } = this.props;
		return (
		<div>{name}</div>
		<div>{sort}</div>
		)
	}
}

除開外部資料,元件自身也需要一個小倉庫來儲存自身的狀態與資料。而state就是用來記錄、改變元件內部資料狀態。在react裡,只需要更新元件的state,然後根據新的state重新渲染使用者介面(不要操作DOM)

如何定義的簡單例子:

export default class Index extends Component{
// 通過建構函式來初始化state
	construct(props){
		super(props);
		this.state = {
			timeout: new Date();
		}
  	}

	changeTime(){
	// 改變state的值
		this.setState({
			timeout: new Date();
		})
	}

	render(){
		return(
		// 獲取timeout的值
			<span>this.state.timeout.toLocalString()</span>
			<Button OnClick={this.changeTime.bind(this)}>改變timeout</Button>
		)
	}
}

現在我們知道了怎樣使用state了。那麼接下來我們需要知道,如何定義好一個state。
正確建立一個元件的第一步就是定義合適的state,我們不能將元件中所有的值都通過state來儲存,造成state的濫用,一是影響程式碼結構,二會影響效能(一個setState會經歷四個生命週期)。

react中state的特點
1.是非同步操作的函式
2.元件在還沒有渲染之前,this.setState還沒有被呼叫。
3.批量執行State轉變時讓DOM渲染更快(對比一個個執行state)

state應該能提現以下方面
1.state中的資料應該是能體現出元件UI每一次狀態的變化,且是每一個元件UI呈現出來的狀態集。
2.state的資料,不應該通過其他狀態來計算出中間狀態。

所以,在每一次需要在state中定義狀態之前,我們都應該去考慮以下幾點:
1.我們定義的state中的值是否可以通過上層元件拿到,或者說可以從其他state狀態中計算而得,如果是,放棄新增。
2.我們定義的state是否不需要在render方法中執行,如果是,我們可以考慮在元件的全域性定義一個普通屬性。
3.我們定義的state是否在整個生命週期中都保持不變,如果是,放棄新增。

我們知道了如何正確使用,但還需使用時的注意事項。

使用state我們應該注意哪些事項?
1.state是不可以直接修改的,比如說要修改一條資料,使用如下方式:
this.state.time = new Date();
這種情況是絕不允許的,更改state中的狀態必須使用setState()函式。

2.state是非同步執行的。
舉個例子,假設有一個購物車,當點選一次購買按鈕,購買的數量就會加1,如果我們連續點選了兩次按鈕,就會連續呼叫兩次this.setState({quantity: this.state.quantity + 1}),但是在React中,合併多次修改為一次的情況下,相當於等價執行了如下程式碼:
Object.assign(
previousState,
{quantity: this.state.quantity + 1},
{quantity: this.state.quantity + 1}
)

後面的操作覆蓋掉了前面的操作,最終購買的數量只增加了1個。

我們要明白的是,在react中,setState是非同步執行的,會將待改變的狀態新增到佇列中,比較更新,找到一個特定的時機,執行一次setState()方法。我們在每一次呼叫setState()這個函式時,資料並不會立馬就更新,下面來個錯誤示範:

changeState(){
	this.setState({ quantity: this.state.quatity});
	this.setState({ quantity: this.state.quatity});
	this.fetchData(this.state.quatity);
}

在剛剛接觸state時,我們就會像以上那樣寫。這裡存在兩個錯誤,一是setState()這個函式只會執行一次,而是setState()這個是非同步更新的,執行完setState(),作為fetchData的引數並不是立馬就更新了。

如果你真的需要實現以下需求。
(1)下一個資料依賴上一個資料,可以使用另一個接收一個函式作為引數的setState,這個函式有兩個引數,第一個引數是元件的前一個state(本次元件狀態修改成功前的state),第二個引數是元件當前最新的props。

// 錯誤
changeState(){
	this.setState({ quantity: this.state.quatity});
	this.setState({ quantity: this.state.quatity});
}
// 正確
this.setState((preState, props) => ({
  counter: preState.quantity + 1; 
}))

(2)方法需要依賴state的值
執行的方法需要使用state更新後的值。可以使用es6的寫法,當然和第一種情況其實是一樣的。將非同步變成同步。

// 錯誤
changeState(){
	this.setState({ quantity: this.state.quatity});
	this.fetchData(this.state.quatity);
}
// 正確
changeState(){
	this.setState({ quantity: this.state.quatity}).then(()=>{
	this.fetchData(this.state.quatity);
});

(3)一個地方有多個地方setState如何優化?
我們在一個方法裡,有時間很難去避免要執行多個setState()方法。比如下面這樣:

changeOnTab(num){
switch(num){
	case 1:
		this.setState({
		username: mm,
		age: 13,
		})
	break
	case 2:
		this.setState({
			username: dd,
			age: 14,
		})
	break
	case 3:
		this.setState({
			username: dd,
			age: 15,
		})
	break
	}
}

按以上程式碼這樣寫,我們發現會特別冗餘,假設這不是case,那我們需要執行多少次setState方法呢?所以我們可以更改成以下簡便的寫法:

changeOnTab(num){
	const state = {
		username: dd,
		age: 15,
	}
	switch(num){
		case 1:
			state.username = mm,
			state.age = 14
		break
		case 2:
			state.username = mm,
		break
	}
	this.setState(state);
}

參考文件:
https://juejin.im/post/5ad458c7f265da239c7bd37c
http://www.runoob.com/react/react-state.html