state
state 是私有的,并且完全受控于當(dāng)前組件。class組件才有state。
將函數(shù)組件轉(zhuǎn)化成class組件
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
#轉(zhuǎn)化為class組件
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
- 創(chuàng)建一個與函數(shù)同名的類并且繼承于
React.Component。 - 添加一個空的
render()方法, 將函數(shù)體移動到render()方法之中。 - 在
render()方法中使用this.props替換props。
每次組件更新時 render 方法都會被調(diào)用,但只要在相同的 DOM 節(jié)點(diǎn)中渲染 <Clock /> ,就僅有一個 Clock 組件的 class 實例被創(chuàng)建使用。這就使得我們可以使用如 state 或生命周期方法等很多其他特性。
在class組件中添加局部state
class Clock extends React.Component {
constructor(props) {
#Class 組件應(yīng)該始終使用 props 參數(shù)來調(diào)用父類的構(gòu)造函數(shù)。
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
將生命周期方法添加到 Class 中
在具有許多組件的應(yīng)用程序中,當(dāng)組件被銷毀時釋放所占用的資源是非常重要的。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
#當(dāng) `Clock` 組件第一次被渲染到 DOM 中的時候,就為其設(shè)置一個計時器。
#這在 React 中被稱為“掛載(mount)”。
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
#當(dāng) DOM 中 `Clock` 組件被刪除的時候,應(yīng)該清除計時器。
#這在 React 中被稱為“卸載(unmount)”。
clearInterval(this.timerID);
}
tick = () => {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
執(zhí)行順序:
- 當(dāng) <Clock /> 被傳給 ReactDOM.render()的時候,React 會調(diào)用 Clock 組件的構(gòu)造函數(shù)。因為 Clock 需要顯示當(dāng)前的時間,所以它會用一個包含當(dāng)前時間的對象來初始化 this.state。我們會在之后更新 state。
- 之后 React 會調(diào)用組件的 render() 方法。這就是 React 確定該在頁面上展示什么的方式。然后 React 更新 DOM 來匹配 Clock 渲染的輸出。
- 當(dāng) Clock 的輸出被插入到 DOM 中后,React 就會調(diào)用 ComponentDidMount() 生命周期方法。在這個方法中,Clock 組件向瀏覽器請求設(shè)置一個計時器來每秒調(diào)用一次組件的 tick() 方法。
- 瀏覽器每秒都會調(diào)用一次 tick() 方法。 在這方法之中,Clock 組件會通過調(diào)用 setState() 來計劃進(jìn)行一次 UI 更新。得益于 setState() 的調(diào)用,React 能夠知道 state 已經(jīng)改變了,然后會重新調(diào)用 render() 方法來確定頁面上該顯示什么。這一次,render() 方法中的 this.state.date 就不一樣了,如此以來就會渲染輸出更新過的時間。React 也會相應(yīng)的更新 DOM。
- 一旦 Clock 組件從 DOM 中被移除,React 就會調(diào)用 componentWillUnmount() 生命周期方法,這樣計時器就停止了。
State的使用
- 不要直接修改 State
this.state.comment = 'Hello'; // Wrong
#應(yīng)該使用 setState():
this.setState({comment: 'Hello'}); // Correct
- State 的更新可能是異步的
出于性能考慮,React 可能會把多個 setState() 調(diào)用合并成一個調(diào)用。因為 this.props 和 this.state 可能會異步更新,所以你不要依賴他們的值來更新下一個狀態(tài)。
this.setState({
counter: this.state.counter + this.props.increment,
}); // Wrong
#要解決這個問題,可以讓 setState() 接收一個函數(shù)而不是一個對象。這個函數(shù)用上一個 state 作為第一個參數(shù),將此次更新被應(yīng)用時的 props 做為第二個參數(shù):
this.setState((state, props) => ({
counter: state.counter + props.increment
})); // Correct
- State 的更新會被合并
調(diào)用 setState() 的時候,React 會把你提供的對象合并到當(dāng)前的 state。這里的合并是淺合并。
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
# 可以分別調(diào)用 setState() 來單獨(dú)地更新它們
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
數(shù)據(jù)是自頂向下流動的
父組件或子組件都不能知道某個組件是有狀態(tài)還是無狀態(tài),并且它們不應(yīng)該關(guān)心某組件是被定義為一個函數(shù)還是一個class。
這就是為什么state通常被稱為局部或封裝。 除了擁有并設(shè)置它的組件外,其它組件不可訪問。
組件可以選擇把它的 state 作為 props 向下傳遞到它的子組件中。但子組件本身無法知道它是來自于父組件的 state、props、還是手動輸入的。
這通常被稱為“自頂向下”或“單向”數(shù)據(jù)流。 任何狀態(tài)始終由某些特定組件所有,并且從該狀態(tài)導(dǎo)出的任何數(shù)據(jù)或 UI 只能影響樹中下方的組件。
props
state 和 props 主要的區(qū)別在于 props 是不可變的,而 state 可以根據(jù)與用戶交互來改變。
- props 驗證
props 驗證使用 propTypes,它可以保證我們的應(yīng)用組件被正確使用,React.PropTypes 提供很多驗證器 (validator) 來驗證傳入數(shù)據(jù)是否有效。當(dāng)向 props 傳入無效數(shù)據(jù)時,JavaScript 控制臺會拋出警告。
let title = "測試";
// let title = 123;
class TestTitle extends React.Component {
render() {
return (
<h1>Hello, {this.props.title}</h1>
);
}
}
#當(dāng)title不是string時(123),拋出警告。
TestTitle.propTypes = {
title: PropTypes.string
};
ReactDOM.render(
<TestTitle title={title} />,
document.getElementById('root')
);