React屬性(props)和狀態(state)
一、屬性(props)
屬性props是由外部傳入、描述性質的,組件內部也可以通過一些方式來初始化的設置。屬性不能被組件自己更改,但是可以通過父組件主動重新渲染的方式來傳入新的
props
之前的組件代碼里面有
props
的簡單使用,總的來說,在使用一個組件的時候,可以把參數放在標簽的屬性當中,所有的屬性都會作為組件props
對象的鍵值。通過箭頭函數創建的組件,需要通過函數的參數來接收props
- 設置組件的默認props
import React, { Component } from 'react';
class Title extends Component {
//第一種定義方式
static defaultProps = {
name:"react"
}
render() {
return (
<div>
歡迎進入{this.props.name} 的世界
</div>
);
}
}
//第二種方式
// Title.defaultProps = {
// name:'react'
// }
export default Title;
- props.children(插槽)
組件可以嵌套,在自定義組件使用嵌套結構時,需要使用
props.children
。
ReactDOM.render(
<Content>
<h1>lxc</h1>
<Title>
react
</Title>
</Content>,
document.getElementById('root')
);
通過“插槽”進行嵌套內容的接收
import React, { Component } from 'react';
class Content extends Component {
render() {
return (
<div>
{
this.props.children
}
</div>
);
}
}
export default Content;
- 使用prop-types檢查props
React其實是為了構建大型應用程序而生, 在一個大型應用中,根本不知道別人使用你寫的組件的時候會傳入什么樣的參數,有可能會造成應用程序運行不了,但是不報錯。為了解決這個問題,React提供了一種機制,讓寫組件的人可以給組件的
props
設定參數檢查,需要安裝和使用prop-types:
- 安裝prop-types驗證
$ cnpm i prop-types -S
import React, { Component } from 'react';
import Proptypes from 'prop-types'
class Product extends Component {
render() {
return (
<div>
產品名稱: {this.props.name}
</div>
);
}
}
Product.propTypes={
name:Proptypes.string,
//['北京','天津']
// city:Proptypes.arrayOf(Proptypes.string).isRequired,
// customProp:function(props,PropName){
// if(!/gp/.test(props[PropName])){
// return new Error('內容非法')
// }
// console.log(arguments)
// },
customArrayProp: Proptypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/北京/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
}).isRequired
}
export default Product;
二、狀態(state)
狀態就是組件描述某種顯示情況的數據,由組件自己設置和更改,也就是說由組件自己維護,使用狀態的目的就是為了在不同的狀態下使組件的顯示不同(自己管理)
- 定義state
constructor() {
super();
//state定義的第一種方式
//推薦
// this.state = {
// count: 1,
// title: "中國機長"
// }
}
//state定義的第二種方式
state ={
title:"中國機長",
count:1
}
- 修改state的三種方式
- 在React中通過this.setState進行數據的修改。
- this.setState是異步執行的。( 異步轉同步 )
- this.setState中的第二個參數是回調,用來驗證數據是否修改成功,獲取數據更新后的DOM結構。
- 只要this.setState調用render函數就會執行。
import React, { Component } from 'react';
class Movie extends Component {
constructor() {
super();
this.state ={
title:"中國機長",
count:1
}
}
componentDidMount() {
//修改state的第一種方式
// setTimeout(() => {
// this.setState({
// title: "戰狼2"
// })
// }, 2000)
//修改state的第二種方式
// this.state.title ="戰狼2"
// setTimeout(()=>{
// this.setState({})
// },2000)
//修改state的數據是異步的
// this.setState({
// count: this.state.count + 1
// })
// console.log(this.state.count)
//修改state的第三種方式
this.setState((preState, props) => {
console.log(props);
//數據的更新,會做merge
return {
count: preState.count + 1
}
}, () => {
console.log(this.state.count)
})
setTimeout(()=>{
this.setState({
count:3
})
console.log(this.state.title,this.props.city)
},3000)
}
render() {
console.log(1)
return (
<div>
<h1>{this.state.title}</h1>
</div>
);
}
}
export default Movie;
三、屬性vs狀態
- 相似點:
都是純JS對象,都會觸發render更新,都具有確定性(狀態/屬性相同,結果相同)
- 不同點:
- 屬性能從父組件獲取,狀態不能
- 屬性可以由父組件修改,狀態不能
- 屬性能在內部設置默認值,狀態也可以
- 屬性不在組件內部修改,狀態要改
- 屬性能設置子組件初始值,狀態不可以
- 屬性可以修改子組件的值,狀態不可以
state
的主要作用是用于組件保存、控制、修改自己的可變狀態。state
在組件內部初始化,可以被組件自身修改,而外部不能訪問也不能修改。你可以認為state
是一個局部的、只能被組件自身控制的數據源。state
中狀態可以通過this.setState
方法進行更新,setState
會導致組件的重新渲染。
props
的主要作用是讓使用該組件的父組件可以傳入參數來配置該組件。它是外部傳進來的配置參數,組件內部無法控制也無法修改。除非外部組件主動傳入新的props
,否則組件的props
永遠保持不變。如果搞不清
state
和props
的使用場景,記住一個簡單的規則:盡量少地用state
,多用props
。沒有
state
的組件叫無狀態組件(stateless component),設置了 state 的叫做有狀態組件(stateful component)。因為狀態會帶來管理的復雜性,我們盡量多地寫無狀態組件,盡量少地寫有狀態的組件。這樣會降低代碼維護的難度,也會在一定程度上增強組件的可復用性。
四、狀態提升
如果有多個組件共享一個數據,把這個數據放到共同的父級組件中來管理
五、案例(計數器)
- Counter.jsx
import React, { Component } from 'react';
import Button from './Button'
import Count from './Count'
import styled from 'styled-components'
const CountWrapper = styled.div`
h1 {
display:block;
color:red;
}
button {
width:100px;
height:40px;
background:blue;
color:white;
font-size:20px;
}
`
class Counter extends Component {
constructor() {
super();
this.state = {
count: 0
}
}
handleChange(type) {
console.log('handle change')
this.setState((preState, props) => {
if (type === 'increment') {
return {
count: preState.count + 1
}
} else {
return {
count: preState.count - 1
}
}
}, () => { })
}
render() {
return (
<CountWrapper >
<Button change={() => this.handleChange('decrement')}>-</Button>
<Count count={this.state.count}></Count>
<Button change={() => this.handleChange('increment')}>+</Button>
</CountWrapper>
);
}
}
export default Counter;
- Count.jsx
import React from 'react'
import Proptypes from 'prop-types'
class Count extends React.Component {
render(){
return <h1>{this.props.count}</h1>
}
}
Count.propTypes ={
count:Proptypes.number.isRequired
}
export default Count;
- Button.jsx
import React, { Component } from 'react';
class Button extends Component {
handleClick(){
console.log('handle click')
this.props.change()
}
render() {
return (
<button onClick={()=>this.handleClick()}>
{this.props.children}
</button>
);
}
}
export default Button;