React元件的state和props
技術標籤:reactjavascript
React元件的state和props
React
的資料是自頂向下單向流動的,即從父元件到子元件中,元件的資料儲存在props
和state
中。實際上在任何應用中,資料都是必不可少的,我們需要直接的改變頁面上一塊的區域來使得檢視的重新整理,或者間接地改變其他地方的資料,在React
中就使用props
和state
兩個屬性儲存資料。
描述
state
的主要作用是用於元件儲存、控制、修改自己的可變狀態。state
在元件內部初始化,可以被元件自身修改,而外部不能訪問也不能修改,可以認為state
是一個區域性的、只能被元件自身控制的資料來源,state
中狀態可以通過this.setState
setState
會導致元件的重新渲染。props
的主要作用是讓使用該元件的父元件可以傳入引數來配置該元件,它是外部傳進來的配置引數,元件內部無法控制也無法修改,除非外部元件主動傳入新的props
,否則元件的props
永遠保持不變。state
和props
都可以決定元件的行為和顯示形態,一個元件的state
中的資料可以通過props
傳給子元件,一個元件可以使用外部傳入的props
來初始化自己的state
,但是它們的職責其實非常明晰分明,state
是讓元件控制自己的狀態,props
是讓外部對元件自己進行配置。簡單來說props
是傳遞給元件的(類似於函式的形參),而state
是在元件內被元件自己管理的(類似於在一個函式內宣告的變數)。state
,儘量多地用props
,沒有state
的元件叫無狀態元件stateless component
,設定了state
的叫做有狀態元件stateful component
。因為狀態會帶來管理的複雜性,我們儘量多地寫無狀態元件,儘量少地寫有狀態的元件,這樣會降低程式碼維護的難度,也會在一定程度上增強元件的可複用性。
props
React
的核心思想就是元件化思想,頁面會被切分成一些獨立的、可複用的元件。元件從概念上看就是一個函式,可以接受一個引數作為輸入值,這個引數就是props
,所以可以把props
理解為從外部傳入元件內部的資料,由於React
是單向資料流,所以props
假設我們現在需要實現一個列表,我們把列表中的行當做一個元件,也就是有這樣兩個元件
<ItemList/>
和<Item/>
。列表ItemList
元件的資料我們就暫時先假設是放在一個data
變數中,然後通過map
函式返回一個每一項都是<Item item={資料}/>
的陣列,也就是說這裡其實包含了data.length
個<Item/>
元件,資料通過在元件上自定義一個引數傳遞。之後在Item
元件內部是使用this.props
來獲取傳遞到該元件的所有資料,它是一個物件其中包含了所有對這個元件的配置,現在只包含了一個item
屬性,所以通過this.props.item
來獲取即可。
// Item元件
class Item extends React.Component{
render(){
return (
<li>{this.props.item}</li>
)
}
}
// ItemList元件
class ItemList extends React.Component{
render(){
const data = [1, 2, 3, 4, 5, 6];
const itemList = data.map((v, i) => <Item item={v} key={i}/>);
return (
<ul>{itemList}</ul>
)
}
}
props
經常被用作渲染元件和初始化狀態,當一個元件被例項化之後,它的props
是隻讀的,不可改變的。如果props
在渲染過程中可以被改變,會導致這個元件顯示的形態變得不可預測,只有通過父元件重新渲染的方式才可以把新的props
傳入元件中。也就是說props
是一個從外部傳進元件的引數,主要作為就是從父元件向子元件傳遞資料,它具有可讀性和不變性,只能通過外部元件主動傳入新的props
來重新渲染子元件,否則子元件的props
以及展現形式不會改變。
在元件中,我們也可以為props
中的引數設定一個defaultProps
,並且制定它的型別。
import PropTypes from "prop-types";
class Hello extends React.Component{
constructor(props){
super(props);
}
render() {
return (
<div>{this.props.tips}</div>
);
}
}
Hello.propTypes = {
tips: PropTypes.string
};
不同的驗證器型別如下。
import PropTypes from "prop-types";
MyComponent.propTypes = {
// JS原始型別,這些全部預設是可選的
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 可以直接渲染的任何東西,可以是數字、字串、元素或陣列
optionalNode: PropTypes.node,
// React元素
optionalElement: PropTypes.element,
// 指定是某個類的例項
optionalMessage: PropTypes.instanceOf(Message),
// 可以是多個值中的一個
optionalEnum: PropTypes.oneOf(["News", "Photos"]),
// 可以是多種型別中的一個
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 只能是某種型別的陣列
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 具有特定型別屬性值的物件
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 具有相同屬性值的物件
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 必選條件,可以配合其他驗證器,以確保在沒有提供屬性的情況下發出警告
requiredFunc: PropTypes.func.isRequired,
// 必選條件,提供的屬性可以為任意型別
requiredAny: PropTypes.any.isRequired,
// 自定義 oneOfType 驗證器。如果提供的屬性值不匹配的它應該丟擲一個異常
// 注意:不能使用 console.warn 和 throw
customProp: function(props, propName, componentName) {
if (!/matcher/.test(props[propName])) {
return new Error("Not Match");
}
},
// 自定義 arrayOf 或者 objectOf 驗證器。
// 它會呼叫每個陣列或者物件的key,引數前兩個是物件它本身和當前key
// 注意:不能使用 console.warn 和 throw
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matcher/.test(propValue[key])) {
return new Error("Not Match");
}
})
};
state
一個元件的顯示形態可以由資料狀態和外部引數所決定,外部引數也就是props
,而資料狀態就是state
。state
的主要作用是用於元件儲存、控制以及修改自己的狀態,它只能在constructor
中初始化,它算是元件的私有屬性,不可通過外部訪問和修改,只能通過元件內部的this.setState
來修改,修改state
屬性會導致元件的重新渲染。簡單來說就是在元件初始化的時候,通過this.state
給元件設定一個初始的state
,在第一次render
的時候就會用這個資料來渲染元件。
class Hello extends React.Component{
constructor(props){
super(props);
this.state = {
tips: "Hello World!"
}
}
render() {
return (
<div>{this.state.tips}</div>
);
}
}
state
不同於props
的一點是,state
是可以被改變的。不過不可以直接通過this.state= values;
的方式來修改,而需要通過this.setState()
方法來修改state
。例如我們經常會通過非同步操作來獲取資料,我們需要在didMount
生命週期階段來執行非同步操作。
componentDidMount(){
fetch("url")
.then(response => response.json())
.then((data) => {
this.setState({itemList:item});
}
}
當我們呼叫this.setState
方法時,React
會更新元件的資料狀態state
,並且重新呼叫render
方法,也就是會對元件進行重新渲染。setState
接受一個物件或者函式作為第一個引數,只需要傳入需要更新的部分即可,setState
還可以接受第二個引數,它是一個函式,會在setState
呼叫完成並且元件開始重新渲染時被呼叫,可以用來監聽渲染完成。
this.setState({ tips: "data update" });
this.setState({ tips: "data update" }, () => console.log("finished"));
示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React</title>
</head>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
class Item extends React.Component{
render(){
return (
<li>{this.props.item}</li>
)
}
}
class ItemList extends React.Component{
render(){
const data = [1, 2, 3, 4, 5, 6];
const itemList = data.map((v, i) => <Item item={v} key={i}/>);
return (
<ul>{itemList}</ul>
)
}
}
class Hello extends React.Component{
constructor(props){
super(props);
this.state = {
tips: "Hello World!"
}
}
render() {
return (
<div>{this.state.tips}</div>
);
}
}
class App extends React.Component{
render() {
return (
<div>
<Hello />
<ItemList />
</div>
);
}
}
var vm = ReactDOM.render(
<App />,
document.getElementById("root")
);
</script>
</html>
每日一題
https://github.com/WindrunnerMax/EveryDay
參考
https://github.com/axuebin/react-blog/issues/8
https://zh-hans.reactjs.org/docs/faq-state.html
http://huziketang.mangojuice.top/books/react/lesson12