React中有兩個東西會控制component:props和state,props是由父元件傳入,在component中,它是不會被改變的,而要改變的資料會放在我們今天要介紹的state。
state比較像是component內部的變數,有私有(private)與封裝(encapsulated)的概念,只有component可以使用自己的state,有別於props是父元件傳入。
使用state有兩個重點:
這邊引用官網的例子,實作一個時鐘來說明state用法。
當我們把element render到畫面上之後,要再次更新畫面就必須重新執行ReactDOM.render(),所以這邊利用setInterval,每秒重新執行React.render()。
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
// render every second
setInterval(tick, 1000);
建立component來實作Clock,並傳入一個名為date的props。利用每秒setInterval,重新傳入props給Clock,每當Clock收到props更新,就會重新render畫面。
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
};
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
(1) 首先要先在Clock constructor建立state初始值,state是一個object,在裡面設定date變數並把目前時間指定給state。再來,把render裡面,原本從props取得的date,改成從state取date變數。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
};
}
(2) 移除render時,從element傳入的props,React.render也只需要執行一次。
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
(3) 在內部update state,這步驟需要一點說明:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
// 指定this
this.tick = this.tick.bind(this);
}
componentDidMount() {
this.timerID = setInterval(
this.tick,
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
tick() {
this.setState({
date: new Date()
});
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
(1) 在ES6 class寫法中,要在constructor設定state初始值,直接指定object給state。
(2) 當要改變state時,不可以直接指定,必須使用setState(),因為setState()之後才會觸發render更新畫面。
(3) State是一個object,當使用setState()時可以update個別的key。
(4) 如果state會依據props改變,props與state的更新不一定會同步,所以使用setState()傳入的參數就不是要更新的state object,而會是一個傳入 previous state 和 props 的function,並且回傳要更新的state object。下面範例使用ES6 Arrow function,直接回傳function運算後的state。
// Wrong,有可能會抓到不正確props
this.setState({
counter: this.state.counter + this.props.increment
});
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
不一定每個元件都會需要有state,可以視情況使用,如果component不需要保有自己的變數,不需控制Life Cycle,使用stateless component效能也會比較好喔!
前面提到父元件傳props給子元件,子元件可以依據props設定state或是自己定義state,但state只有component自己使用,不會往上傳,所以在React裡只有單向資料流,就像waterfall一樣,永遠是"top-down"。