组件的数据挂载方式有2种props属性、state状态。props用于描述特征和性质,组件自身不能随意更改。state用于描述状态的改变,在不同的状态下使组件的显示不同。
不轻易改变的数据使用props定义,如:height/color
变化的数据使用state定义,如:计数 、开关
属性(props)
- 由外部传入的 - 父组件传递给子组件(子组件通过this.props访问)
在父组件中,通过属性绑定的形式绑定数据给Child组件
// src/Child.jsimport {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.jsimport {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.jsconst 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) // customPropconsole.log(componentName) // Childif(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.stateconsole.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=thisbtn.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=0for (let i = 0; i <= 10000000; i++) {n=i}this.setState({count: n})}
render函数中不能直接调用setState,会导致栈溢出死循环报错,这是因为 render函数本身是用于解析this.state和this.props的,解析时在修改就会矛盾
