React 全栈体系(十六)

第八章 React 扩展

五、Context

1. 代码

javascript 复制代码
/* 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 (
			<div className="grand">
				<h3>我是C组件</h3>
				<h4>我从A组件接收到的用户名:{username},年龄是{age}</h4>
			</div>
		)
	}
} */

function C(){
	return (
		<div className="grand">
			<h3>我是C组件</h3>
			<h4>我从A组件接收到的用户名:
			<Consumer>
				{value => `${value.username},年龄是${value.age}`}
			</Consumer>
			</h4>
		</div>
	)
}

2. 总结

  • 理解

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

  • 使用
bash 复制代码
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>
  • 注意

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

六、组件优化

1. Component 的 2 个问题

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

2. 效率高的做法

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

3. 原因

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

4. 解决

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

5. 代码

javascript 复制代码
/* 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>
		)
	}
}
css 复制代码
/* src/index.css */
.parent{
	background-color: orange;
	padding: 10px;
}
.child{
	background-color: gray;
	margin-top: 30px;
	padding: 10px;
}

七、renderProps

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

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

2. children props

javascript 复制代码
<A>
  <B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到

3. render props

javascript 复制代码
<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}

4. 代码

javascript 复制代码
/* 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>
		)
	}
}
css 复制代码
/* 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. 理解

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

2. 特点

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

3. 使用方式

  • getDerivedStateFromError 配合 componentDidCatch
js 复制代码
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
    console.log(error);
    // 在render之前触发
    // 返回新的state
    return {
        hasError: true,
    };
}

componentDidCatch(error, info) {
    // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}

4. 代码


4.1 Child
javascript 复制代码
/* 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>
		)
	}
}
4.2 Parent
javascript 复制代码
/* 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>
		)
	}
}
4.3 App
javascript 复制代码
/* 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. 几种通信方式

bash 复制代码
1.props:
	(1).children props
	(2).render props
2.消息订阅-发布:
	pubs-sub、event等等
3.集中式管理:
	redux、dva等等
4.conText:
	生产者-消费者模式

3. 比较好的搭配方式

  • 父子组件:props
  • 兄弟组件:消息订阅-发布、集中式管理
  • 祖孙组件(跨级组件):消息订阅-发布、集中式管理、Context(开发用的少,封装插件用的多)
相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax