React相关扩展二(Fragment、Content、useContext、组件优化、render props、错误边界)(十)

系列文章目录

第一章:React基础知识(React基本使用、JSX语法、React模块化与组件化)(一)
第二章:React基础知识(组件实例三大核心属性state、props、refs)(二)
第三章:React基础知识(事件处理、受控组件与非受控组件、高阶函数、组件的生命周期)(三)
第四章:React脚手架应用(创建脚手架、代理配置、ajax相关、组件通信)(四)
第五章:react-router5路由相关一(路由相关概念、基本使用、NavLink与NavLink的封装、Switch的使用、严格匹配、路由重定向、路由组件与一般组件的区别)(五)
第六章:react-router5路由相关二(嵌套路由、路由传参、replace、编程式路由导航、withRouter的使用、BrowserRouter与HashRouter的区别)(六)
第七章:React-Router6路由相关一(路由的基本使用、重定向、NavLink·、路由表、嵌套路由)(七)
第八章:React-Router6路由相关二(路由传参、编程式路由导航、路由相关hooks)(八)
第九章:React相关扩展一(setState、lazyLoad、Hooks相关)(九)
第十章:React相关扩展二(Fragment、Content、组件优化、render props、错误边界)(十)
第十一章:Redux相关知识(什么是redux、redux的工作原理、redux的核心概念、redux的基本使用)(十一)
第十二章:React-Redux相关知识(什么是react-redux、react-redux的原理、react-redux相关API、react-redux的基本使用)(十二)



一、 Fragment

(1) 作用

可以不用必须有一个真实的DOM根标签了可以将内部内容当做一个整体,而不产生其他标签,项目运行后会自动删除Fragment标签

(2) 使用

<Fragment><Fragment>
<></>

代码案例:
Demo.jsx

import React, { Component,Fragment } from 'react'

export default class Demo extends Component {
	render() {
		return (
			<Fragment key={1}>
				<input type="text"/>
				<input type="text"/>
			</Fragment>
		)
	}
}

App.js

import React, { Component, Fragment } from 'react'
import Demo from './components/3_hooks'

export default class App extends Component {
	render () {
		return (
			<>
				<Demo />
			</>
		)
	}
}


二、Context

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

2.1 使用

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插件

代码案例:

import React, { Component } from 'react'
import './index.css'
import './B.tsx'

//创建Context对象
export const MyContext = React.createContext()
const {Provider,Consumer} = MyContext

//A.tsx
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>
		)
	}
}
//B.tsx
import React, { Component } from 'react'
import './index.css'

export default class B extends Component {
	render() {
		return (
			<div className="child">
				<h3>我是B组件</h3>
				<C/>
			</div>
		)
	}
}

//c.tsx
import React, { Component } from 'react'
import './index.css'

//创建Context对象
import { MyContext } from './A'
const {Consumer} = MyContext

/*export default 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>
		)
	}
} */

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

运行结果:

在这里插入图片描述


三、useContext

与 Context 一样,在 React Hooks 中也提供了更加高级的一种组件中传递值的方式,不再需要一层一层的向下传递,而是可以隔层传递。
1) 创建Context容器对象:

  export const XxxContext = React.createContext()  

2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

	<xxxContext.Provider value={数据}>
		子组件
    </xxxContext.Provider>

3) 后代组件读取数据:

import {xxxContext } from "./XXX";
	  
const { x } = useContext(xxxContext);
render(){
  return (
    <div>
      	{x}
    </div>
  );
}

代码案例片段:

A.jsx

import React, { Component } from "react";
import "./index.css";
import B from "./B";
//创建Context对象
export const MyContext = React.createContext();
const { Provider } = 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>
    );
  }
}

B.jsx

import React from "react";
import "./index.css";
import C from "./C";

const B = () => {
  return (
    <div className="child">
      <h3>我是B组件</h3>
      <C />
    </div>
  );
};

export default B;

C.jsx

import React, { useContext } from "react";
import "./index.css";
import { MyContext } from "./A";

const C = () => {
  const { username, age } = useContext(MyContext);
  return (
    <div className="grand">
      <h3>我是C组件</h3>
      <h4>
        我从A组件接收到的用户名:
        {username},年龄是{age}
      </h4>
    </div>
  );
};

export default C;

运行结果:

在这里插入图片描述

四、 组件优化

4.1 Component的2个问题

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render()

  2. 只当前组件重新render(), 就会自动重新render子组件 ==> 效率低

4.2 效率高的做法

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

4.3 原因

Component中的shouldComponentUpdate()总是返回true

4.4 解决

  • 办法1:

    • 重写shouldComponentUpdate()方法

    • 比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false

    代码案例片段:

    import React, { Component } from "react";
    import "./index.css";
    
    export default class Parent extends Component {
      state = { carName: "奔驰c36", stus: ["小张", "小李", "小王"] };
    
      addStu = () => {
        /* const {stus} = this.state
    		stus.unshift('小刘')//不要直接修改state数据, 而是要产生新数据,无法触发页面渲染
    		this.setState({stus})
    	 */
    
        const { stus } = this.state;
        this.setState({ stus: ["小刘", ...stus] });
      };
    
      changeCar = () => {
        this.setState({ carName: "迈巴赫" });
    
        // const obj = this.state;
        // obj.carName = "迈巴赫";//不要直接修改state数据, 而是要产生新数据,无法触发页面渲染
        // console.log(obj === this.state);//true
        // 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; //如果车名没有发生改变,则返回false,阻止渲染,这样虽然添加了一个小李,但是页面也不会渲染
      }
    
      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={carName} />
          </div>
        );
      }
    }
    
    class Child extends Component {
      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>
        );
      }
    }
    

    运行结果:
    在这里插入图片描述
    在这里插入图片描述

  • 办法2:

    • 使用PureComponent

    • PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true

      项目中一般使用PureComponent来优化

    代码案例片段:

    import React, { PureComponent } from "react";
    import "./index.css";
    
    export default class Parent extends PureComponent {
      state = { carName: "奔驰c36", stus: ["小张", "小李", "小王"] };
    
      addStu = () => {
        const { stus } = this.state;
        this.setState({ stus: ["小刘", ...stus] });
      };
    
      changeCar = () => {
        this.setState({ 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={carName} />
          </div>
        );
      }
    }
    
    class Child extends PureComponent {
    
      render() {
        console.log("Child---render");
        return (
          <div className="child">
            <h3>我是Child组件</h3>
            <span>我接到的车是:{this.props.carName}</span>
          </div>
        );
      }
    }
    

    运行结果:
    在这里插入图片描述
    在这里插入图片描述

    注意:

    • 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false

    • 不要直接修改state数据, 而是要产生新数据


五、render props

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

Vue中:

  • 使用slot技术, 也就是通过组件标签体传入结构 <AA><BB/></AA>

React中:

  • 使用children props: 通过组件标签体传入结构
  • 使用render props: 通过组件标签属性传入结构, 一般用render函数属性

5.2 children props

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

5.3 render props

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

代码案例片段:

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("A--render", 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", this.props);
    return (
      <div className="b">
        <h3>我是B组件,{this.props.name}</h3>
      </div>
    );
  }
}

运行结果:
在这里插入图片描述


六、错误边界

6.1 理解:

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

6.2 特点:

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

使用方式:

getDerivedStateFromError配合componentDidCatch

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

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

代码案例片段:

Children.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>
    );
  }
}

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>
		)
	}
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值