• React 全栈体系(十六)


    第八章 React 扩展

    五、Context

    请添加图片描述

    1. 代码

    /* index.jsx */
    import React, { Component } from 'react'
    import './index.css'
    
    //创建Context对象
    const MyContext = React.createContext()
    const {Provider,Consumer} = MyContext
    export default class A extends Component {
    
    	state = {username:'tom',age:18}
    
    	render() {
    		const {username,age} = this.state
    		return (
    			<div className="parent">
    				<h3>我是A组件</h3>
    				<h4>我的用户名是:{username}</h4>
    				<Provider value={{username,age}}>
    					<B/>
    				</Provider>
    			</div>
    		)
    	}
    }
    
    class B extends Component {
    	render() {
    		return (
    			<div className="child">
    				<h3>我是B组件</h3>
    				<C/>
    			</div>
    		)
    	}
    }
    
    /* class C extends Component {
    	//声明接收context
    	static contextType = MyContext
    	render() {
    		const {username,age} = this.context
    		return (
    			

    我是C组件

    我从A组件接收到的用户名:{username},年龄是{age}

    ) } } */
    function C(){ return ( <div className="grand"> <h3>我是C组件</h3> <h4>我从A组件接收到的用户名: <Consumer> {value => `${value.username},年龄是${value.age}`} </Consumer> </h4> </div> ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    2. 总结

    • 理解

    一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

    • 使用
    1) 创建Context容器对象:
    	const XxxContext = React.createContext()
    
    2) 渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
    	<xxxContext.Provider value={数据}>
    		子组件
        </xxxContext.Provider>
    
    3) 后代组件读取数据:
    
    	//第一种方式:仅适用于类组件
    	  static contextType = xxxContext  // 声明接收context
    	  this.context // 读取context中的value数据
    
    	//第二种方式: 函数组件与类组件都可以
    	  <xxxContext.Consumer>
    	    {
    	      value => ( // value就是context中的value数据
    	        要显示的内容
    	      )
    	    }
    	  </xxxContext.Consumer>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 注意

    应用开发中一般不用 context, 一般都用它的封装 react 插件

    六、组件优化

    1. Component 的 2 个问题

    • 只要执行 setState(),即使不改变状态数据, 组件也会重新 render() ==> 效率低
    • 只当前组件重新 render(), 就会自动重新 render 子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

    2. 效率高的做法

    • 只有当组件的 state 或 props 数据发生改变时才重新 render()

    3. 原因

    • Component 中的 shouldComponentUpdate()总是返回 true

    4. 解决

    办法1:
    	重写shouldComponentUpdate()方法
    	比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
    办法2:
    	使用PureComponent
    	PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
    	注意:
    		只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
    		不要直接修改state数据, 而是要产生新数据
    项目中一般使用PureComponent来优化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5. 代码

    请添加图片描述

    /* src/App.jsx */
    import React, { PureComponent } from 'react'
    import './index.css'
    
    export default class Parent extends PureComponent {
    
    	state = {carName:"奔驰c36",stus:['小张','小李','小王']}
    
    	addStu = ()=>{
    		/* const {stus} = this.state
    		stus.unshift('小刘')
    		this.setState({stus}) */
    
    		const {stus} = this.state
    		this.setState({stus:['小刘',...stus]})
    	}
    
    	changeCar = ()=>{
    		//this.setState({carName:'迈巴赫'})
    
    		const obj = this.state
    		obj.carName = '迈巴赫'
    		console.log(obj === this.state);
    		this.setState(obj)
    	}
    
    	/* shouldComponentUpdate(nextProps,nextState){
    		// console.log(this.props,this.state); //目前的props和state
    		// console.log(nextProps,nextState); //接下要变化的目标props,目标state
    		return !this.state.carName === nextState.carName
    	} */
    
    	render() {
    		console.log('Parent---render');
    		const {carName} = this.state
    		return (
    			<div className="parent">
    				<h3>我是Parent组件</h3>
    				{this.state.stus}&nbsp;
    				<span>我的车名字是:{carName}</span><br/>
    				<button onClick={this.changeCar}>点我换车</button>
    				<button onClick={this.addStu}>添加一个小刘</button>
    				<Child carName="奥拓"/>
    			</div>
    		)
    	}
    }
    
    class Child extends PureComponent {
    
    	/* shouldComponentUpdate(nextProps,nextState){
    		console.log(this.props,this.state); //目前的props和state
    		console.log(nextProps,nextState); //接下要变化的目标props,目标state
    		return !this.props.carName === nextProps.carName
    	} */
    
    	render() {
    		console.log('Child---render');
    		return (
    			<div className="child">
    				<h3>我是Child组件</h3>
    				<span>我接到的车是:{this.props.carName}</span>
    			</div>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    /* src/index.css */
    .parent{
    	background-color: orange;
    	padding: 10px;
    }
    .child{
    	background-color: gray;
    	margin-top: 30px;
    	padding: 10px;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    七、renderProps

    请添加图片描述

    1. 如何向组件内部动态传入带内容的结构(标签)?

    • Vue 中:
      • 使用 slot 技术, 也就是通过组件标签体传入结构
    • React 中:
      • 使用 children props: 通过组件标签体传入结构
      • 使用 render props: 通过组件标签属性传入结构,而且可以携带数据,一般用 render 函数属性

    2. children props

    <A>
      <B>xxxx</B>
    </A>
    {this.props.children}
    问题: 如果B组件需要A组件内的数据, ==> 做不到
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3. render props

    <A render={(data) => <C data={data}></C>}></A>
    A组件: {this.props.render(内部state数据)}
    C组件: 读取A组件传入的数据显示 {this.props.data}
    
    • 1
    • 2
    • 3

    4. 代码

    /* src/App.jsx */
    import React, { Component } from 'react'
    import './index.css'
    
    export default class Parent extends Component {
    	render() {
    		return (
    			<div className="parent">
    				<h3>我是Parent组件</h3>
    				<A render={(name)=><B name={name}/>}/>
    			</div>
    		)
    	}
    }
    
    class A extends Component {
    	state = {name:'tom'}
    	render() {
    		console.log(this.props);
    		const {name} = this.state
    		return (
    			<div className="a">
    				<h3>我是A组件</h3>
    				{this.props.render(name)}
    			</div>
    		)
    	}
    }
    
    class B extends Component {
    	render() {
    		console.log('B--render');
    		return (
    			<div className="b">
    				<h3>我是B组件,{this.props.name}</h3>
    			</div>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    /* src/index.css */
    .parent{
    	background-color: orange;
    	padding: 10px;
    }
    .a{
    	background-color: gray;
    	margin-top: 30px;
    	padding: 10px;
    }
    .b{
    	background-color: skyblue;
    	margin-top: 30px;
    	padding: 10px;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    八、错误边界

    1. 理解

    • 错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

    2. 特点

    • 只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

    3. 使用方式

    • getDerivedStateFromError 配合 componentDidCatch
    // 生命周期函数,一旦后台组件报错,就会触发
    static getDerivedStateFromError(error) {
        console.log(error);
        // 在render之前触发
        // 返回新的state
        return {
            hasError: true,
        };
    }
    
    componentDidCatch(error, info) {
        // 统计页面的错误。发送请求发送到后台去
        console.log(error, info);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4. 代码

    请添加图片描述
    请添加图片描述

    4.1 Child
    /* src/Child.jsx */
    import React, { Component } from 'react'
    
    export default class Child extends Component {
    	state = {
    		users:[
    			{id:'001',name:'tom',age:18},
    			{id:'002',name:'jack',age:19},
    			{id:'003',name:'peiqi',age:20},
    		]
    		// users:'abc'
    	}
    
    	render() {
    		return (
    			<div>
    				<h2>我是Child组件</h2>
    				{
    					this.state.users.map((userObj)=>{
    						return <h4 key={userObj.id}>{userObj.name}----{userObj.age}</h4>
    					})
    				}
    			</div>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    4.2 Parent
    /* src/Parent.jsx */
    import React, { Component } from 'react'
    import Child from './Child'
    
    export default class Parent extends Component {
    
    	state = {
    		hasError:'' //用于标识子组件是否产生错误
    	}
    
    	//当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
    	static getDerivedStateFromError(error){
    		console.log('@@@',error);
    		return {hasError:error}
    	}
    
    	componentDidCatch(){
    		console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
    	}
    
    	render() {
    		return (
    			<div>
    				<h2>我是Parent组件</h2>
    				{this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child/>}
    			</div>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    4.3 App
    /* src/App.jsx */
    import React, { Component,Fragment } from 'react'
    import Demo from './Parent'
    
    export default class App extends Component {
    	render() {
    		return (
    			<Fragment>
    				<Demo/>
    			</Fragment>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    九、组件通信方式总结

    1. 组件间的关系

    • 父子组件
    • 兄弟组件(非嵌套组件)
    • 祖孙组件(跨级组件)

    2. 几种通信方式

    1.props:
    	(1).children props
    	(2).render props
    2.消息订阅-发布:
    	pubs-sub、event等等
    3.集中式管理:
    	redux、dva等等
    4.conText:
    	生产者-消费者模式
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3. 比较好的搭配方式

    • 父子组件:props
    • 兄弟组件:消息订阅-发布、集中式管理
    • 祖孙组件(跨级组件):消息订阅-发布、集中式管理、Context(开发用的少,封装插件用的多)
  • 相关阅读:
    Python | R | MATLAB群体消息和遗传病筛选多元统计模型
    开发知识点-golang
    docker介绍及使用
    微信小程序瀑布流组件
    基于机器学习和深度学习的C-MAPSS涡扇发动机剩余寿命RUL预测(Python,Jupyter Notebook环境)
    用Python把csv文件批量修改编码为UTF-8格式并转为Excel格式
    无代码集成API服务平台连接更多应用
    紫光展锐发布全新6G白皮书,展望泛在融合发展蓝图
    实践历练的力量
    Springboot搭建微服务案例之Eureka注册中心
  • 原文地址:https://blog.csdn.net/sgsgkxkx/article/details/133318514