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;