组件的数据挂载方式有2种props
属性、state
状态。props用于描述特征和性质,组件自身不能随意更改。state用于描述状态的改变,在不同的状态下使组件的显示不同。
不轻易改变的数据使用props定义,如:height/color
变化的数据使用state定义,如:计数 、开关
属性(props)
- 由外部传入的 - 父组件传递给子组件(子组件通过this.props访问)
在父组件中,通过属性绑定的形式绑定数据给Child组件
// src/Child.js
import {Component} from 'react'
export default class Child extends Component{
render(){
console.log(this.props) // {content: "内容", name: "syukinmei", age: 18}
return(
<>
<p>{this.props.content}</p>
<p>{this.props.name}</p>
</>
)
}
}
// src/App.js
import {Component} from 'react'
import Child from './Child'
export default class App extends Component{
render(){
return(
<div>
React
<Child content='内容' name="syukinmei" age={18}/>
</div>
)
}
}
当子组件是函数组件时
// src/Child.js
const Child = (props) => {
console.log(props) // {content: "内容", name: "syukinmei", age: 18}
return (
<>
<p>{props.content}</p>
<p>{props.name}</p>
</>
)
}
export default Child
总结:
在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为组件 props 对象的键值。通过箭头函数创建的组件,需要通过函数的参数来接收props
- 类组件自身定义数据 - 组件的默认props
类组件通过static defaultProps={}
关键字 static关键字只有在class类中有所有只能运用于类组件
import {Component} from 'react'
export default class App extends Component{
render(){
return(
<div>
React
<Child content='内容' name="syukinmei" age={18} title='我有标题'/>
</div>
)
}
}
class Child extends Component{
static defaultProps={
title:'默认标题'
}
render(){
console.log(this.props) // {content: "内容", name: "syukinmei", age: 18, title: "我有标题"}
return(
<>
<h1>{this.props.title}</h1>
</>
)
}
}
// 写法2 将Line14~16移出来写
// 因为静态属性可以直接通过类访问或者设置
Child.defaultProps={
title:'默认标题'
}
由于设置了defaultProps,如果Line7中没有绑定title属性则Line18打印
{content: “内容”, name: “syukinmei”, age: 18, title: “默认标题”}
渲染到页面的将会是 默认标题
属性验证
react是为了构建大型应用程序而生的,在一个大型应用中,根本不知道别人使用你写的组件的时候会传入什么样的参数,有可能造成应用程序运行不了,但是不报错。为了解决这个问题,React提供了一种机制,让写组件的人可以给组件的 props
设定参数检测,需要安装和使用 prop-types
第三方插件
$ yarn add prop-types -S $ cnpm i prop-types -S
import Types from 'prop-types'
console.log('types',Types) // Types是一个对象
export default class Child extends Component{
render(){
// ... do things with the props
}
}
Child.propTypes={
content:Types.string, // 验证类型
name:Types.string,
age:Types.number,
title:Types.string,
customProp(props,propName,componentName){
console.log(props) // 所有属性 {content: "内容", name: "syukinmei", age: 18, title: "默认标题"}
console.log(propName) // customProp
console.log(componentName) // Child
if(props.age<18){
alert('未成年')
}
}
}
状态(state)
state定义方法有两种:构造函数中定义和class中直接定义
import { Component } from 'react'
export default class App extends Component {
// 构造函数中定义 (推荐)
constructor() {
super()
this.state = {
name: 'syukinmei'
}
}
// class中直接定义
state = {
name: 'syukinmei'
}
render() {
console.log(this)
const {name}=this.state // 使用ES6中的解构
return (
<div>
{this.state.name}<br/>
{name} // 使用ES6中的解构
</div>
)
}
}
setState
this.props
和this.state
是纯js对象,在vue中,data属性是利用Object.defineProperty
处理过的,更改data的数据的时候会触发数据的getter
和setter
,但是React中没有做这样的处理,如果直接更改的话,react是无法得知的,所以,需要使用特殊的更改状态的方法setState
import { Component } from 'react'
export default class App extends Component {
constructor() {
super()
this.state = {
Flag: true
}
}
showName = () => {
this.setState({
Flag: !this.state.Flag
})
}
render() {
const { Flag } = this.state
console.log(Flag)
return (
<div>
<button onClick={this.showName}>changeName</button>
{Flag ? <p>syukinmei</p> : <p>ebiebi</p>}
</div>
)
}
}
setState有两个参数
第一个参数可以是对象,也可以是方法return一个对象,我们把这个参数叫做updater
参数是对象
this.setState({
Flag: !this.state.Flag
})
参数是方法
需要使用上一个状态(原值)时使用
注意:这个方法接受两个参数,第一个参数用于记录上一个状态(state),第二个参数是props
this.setState((prevState)=>{
console.log(prevState) // {Flag: true, num: 1}
return {
Flag: !prevState.Flag
}
})
第二个参数是一个可选的回调函数
setState是异步的,要想获取到最新的state就需要使用第二个参数
showName = () => {
this.setState((prevState,props)=>{
console.log(1,prevState)
return {
Flag: !prevState.Flag
}
},()=>{
console.log('2',this.state.Flag)
})
console.log('3',this.state.Flag)
}
事件触发时的打印结果
tips:setState会导致组件重新渲染在2之前会执行render()
合成事件中是异步,原生事件中是同步
componentDidMount(){
const btn =document.querySelector('.btn')
const _this=this
btn.onclick=function(){
console.log('原生事件',this) // this指向事件源btn而非组件实例 所以要加Line3操作
_this.setState({
title:'syukinmei'
})
document.title=_this.state.title
}
}
setState内置优化行为
我们使用一个for循环模拟1000万次简单的setState操作
setState —> data change —>虚拟DOM重新生成 —>diff算法比对新旧虚拟DOM得到patch补丁对象,然后在重新将补丁对象渲染为真实DOM
如果短时间进行大量的setState操作React不会立即执行虚拟DOM生成,而是设置一个计划时间,在这个计划时间内所有的setState操作全部会放入一个队列中统一进行处理 合并setState
,使得计划时间内只进行了一次虚拟DOM生成 链接
总结:React对setState已经进行了合并优化处理但是还不够性能损耗也不小,要减少setState操作如下例Line14。
add = () => {
for (let i = 0; i <= 10000000; i++) {
this.setState({
count: i
})
}
}
render(){
console.log('渲染') //开始执行一次 setState操作导致执行一次
return(...)
}
// 优化
add = () => {
let n=0
for (let i = 0; i <= 10000000; i++) {
n=i
}
this.setState({
count: n
})
}
render函数中不能直接调用setState,会导致栈溢出死循环报错,这是因为 render函数本身是用于解析this.state和this.props的,解析时在修改就会矛盾