React教程详解四(hooks、pureComponent、Context通信、错误边界、children props与render props)

前言

hooks是react16.8.0版本新增加的新特性/新语法,最大的特点是可以在开发者在函数组件中使用state以及其它React特性,下面分别对其介绍~

React.useState()

state hook能让函数组件也可以拥有state状态,方便其进行state状态的读写操作~

React.useState()方法的参数为第一次初始化state的值,返回一个包含两个元素的数组,第一个为内部当前状态值,另一个参数是更新状态值的函数

const [xxx, setXXX] = React.useState(initValue)

setXXX方法有两种写法:

setXXX(newValue) // 非函数参数,直接将此值作为新的state值
setXXX(value => newValue) // 函数作为参数,接收原来的状态值,返回新的状态值

setXXX方法会自动将内存中储存的state值进行更新~

// components/Students.jsx
import React from 'react'

export default function Students() {
    const [msg, setMsg] = React.useState({name: '小明', age: 12})
    const [grade, setGrade] = React.useState('99') // 可通过useState传递多个状态
    function show() {
        // setMsg({name: '小红', age: 13}) // 直接将新的状态值返回
        setMsg((old) => ({name: '小红', age: old.age+1})) // 参数返回新的状态值,可对旧状态值进行操作
    }
    function change() {
        setGrade('100')
    }
  return (
    <div>
        <h2>这是学生信息</h2>
        <h3>姓名:{msg.name}</h3>
        <h3>年龄:{msg.age}</h3>
        <h3>成绩:{grade}</h3>
        <button onClick={show}>更改学生信息</button>
        <button onClick={change}>更改学生成绩</button>
    </div>
  )
}

React.useEffect()

Effect hook可以让你在函数组件中使用,达到使用声明周期函数效果;

useEffect(() => {
    // 做一些操作
    return () => { 
        // 如果写了return,则该函数可以在卸载前被调用,
        // 相当于类组件中的componentWillUnmount
     }
},[stateValue] // 此处若指定了stateValue,则就会在组件render和更新时被调用,若指定为空数组时,则只在渲染时被调用
)

可以讲useEffect hook看作是三个声明函数的组合:

componentDidMount()
componnetDidUpdate()
componentWillUnmount()
import React from 'react'
// import ReactDOM from 'react-dom';
import {root} from '../index'

export default function Test() {
  const [number, setNumber] = React.useState(0)
  function add() {
    setNumber(value => value+1)
  }
  function destory() {
    root.unmount()
  }
React.useEffect(() => {
    const timer = setInterval(() => {
        setNumber(value => value+1)
    }, 500)
    return () => {clearInterval(timer)}
},[])
  return (
    <div>
        <span>test组件</span>
        <span>数值是{number}</span>
        <button onClick={add}>点我加一</button>
        <button onClick={destory}>销毁组件</button>
    </div>
  )
}

React.useRef()

ref hook可以在函数组件中保存标签对象,功能与类组件标签中的React.createRef()一样React教程详解一(props、state、refs、生命周期)_迷糊的小小淘的博客-CSDN博客

const xxxRef = React.useRef() // 定义一个ref

在标签中标记只需使用ref=xxxRef即可,取值利用xxxRef.current

import React from 'react'

export default function Students() {
    const [msg, setMsg] = React.useState({name: '小明', age: 12})
    const ageRef = React.useRef() // 定义标签ref
    function show() {
       alert(ageRef.current.innerText); // 取值
    }
  return (
    <div>
        <h2>这是学生信息</h2>
        <h3>姓名:{msg.name}</h3>
        <h3 ref={ageRef}>年龄:{msg.age}</h3>
        <button onClick={show}>显示学生年龄</button>
    </div>
  )
}

其它关于路由的hooks已在上一篇文章中讲过,请移步React教程详解二(脚手架、路由)_迷糊的小小淘的博客-CSDN博客

PureComponent

在使用类组件式,会继承于React的Component组件,该组件存在两个问题:

  • 只要执行setState(),即使不改变状态数据,组件也会重新渲染(render)
  • 若当前组件重新render,即使子组件并未发生任何变化,也会重新渲染

这两个问题导致组件经常被渲染,导致效率低下,因此想要让其当组件的props或state数据发生变化时才重新渲染;

导致此问题的原因在于Component中的shouldComponentUpdate()总是返回true,因此要让其有选择性的进行返回,有两种方法可以做到:

  • 重写每个组件的shouldComponentUpdate(nextProps, nextState)方法,该方法接收未来的props和state,将其与目前的state/props进行比较,若发生改变才返回true,否则为false
import React, { Component } from 'react'

export default class Students extends Component {
    state = {
        name: '小明', 
        age: 10
    }
    show = () => {
        this.setState({name: '小明', age: 11})
    }
    shouldComponentUpdate(nextProps, nextState) {
        return !(nextState.name === this.state.name && nextState.age === this.state.age)
    }
  render() {
    console.log('是否被渲染'); // state中age改变了,所以此行会被输出
    const {name, age} = this.state
    return (
        <div>
            <h2>这是学生信息</h2>
            <h3>姓名:{name}</h3>
            <h3>年龄:{age}</h3>
            <button onClick={this.show}>显示学生年龄</button>
        </div>
    )
  }
}
  • 使用PureComponent替换Component,PureComponent重写了shouldComponentUpdate方法,保证组件只有在state或者props变化的时候返回true(当然是进行了浅比较,所以若直接修改了state也是会引起页面渲染的)
import React, { PureComponent } from 'react'

export default class Students extends PureComponent {
    state = {
        name: '小明', 
        age: 10
    }
    show = () => {
        this.setState({name: '小明', age: 10})
    }
  render() {
    console.log('是否被渲染'); // state中age未改变,所以此行不会被输出
    const {name, age} = this.state
    return (
        <div>
            <h2>这是学生信息</h2>
            <h3>姓名:{name}</h3>
            <h3>年龄:{age}</h3>
            <button onClick={this.show}>显示学生年龄</button>
        </div>
    )
  }
}


开发中常用第二种方法提高运行效率~

Context组件通信

Context也是一种组件通信方式,常用于【祖组件】与【后代组件】通信,但还是redux香哈哈哈哈

使用时要先创建一个Context对象:

const xxxContext = React.createContext() // 创建Contex容器对象

主要利用Context对象上的Provider与Consume两个标签; 

在后代组件外部包裹xxxContext.Provider,并在该标签上添加value属性,此值即为要传给【后代组件】的数据;

const {Provider} = xxxContext  // 在祖组件中使用Provider

<Provider value={{school: '清华'}}>
  子组件
</Provider> // 包裹后代组件

【后代组件】读取数据有两种方式:

  • 在要接收数据的子组件声明static变量再读取---适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中value数据
  • 使用Consumer组件获取数据----类组件与函数组件都可以使用
const { Consumer } = xxxContext  // 在子组件中使用Consumer

<Consumer>
  {
      value => { // value就是context中的value数据
        // 要展示的内容
       }
   }
</Consumer>

示例:

①子组件是类组件形式:

// pages/School.jsx
import React, { Component } from 'react'
import Teacher from './Teacher'
import { SchoolContext } from './Students' // 因为要先加载子组件,所以将创建context定义在子组件上,在祖组件上引入
export const {Provider, Consumer} = SchoolContext

export default class School extends Component {
  state = {
    school: '清华',
    base: '北京'
  }
  render() {
    const {school, base} = this.state
    return (
      <div style={{backgroundColor: 'blue', padding: '10px'}}>
        <h1>这是学校:{school}</h1>
        <Provider value={{school, base}}> // 利用value传过去数据
          <Teacher />
        </Provider>
      </div>
    )
  }
}

// pages/Teacher.jsx 
import React, { Component } from 'react'
import Students from "./Students"
export default class Teacher extends Component {
  render() {
    return (
      <div style={{backgroundColor: 'yellow', padding: '10px'}}>
        <h2>这是老师</h2>
        <Students />
      </div>
    )
  }
}

//pages/Students.jsx
import React, { Component } from 'react'
export const SchoolContext = React.createContext() // 创建context对象并暴露出去
export default class Students extends Component {
  static contextType = SchoolContext
  render() {
    const {school, base} = this.context // 传过来的参数放在组件实例对象的context里
    return (
      <div style={{backgroundColor: 'orange', padding: '10px'}}>
        <h2>该生来自{base}的{school}</h2>
      </div>
    )
  }
}

②子组件是函数组件形式:

// pages/School.jsx
import React, { Component } from 'react'
import Teacher from './Teacher'
import { Provider } from './Students' // 引入context对象中的Provider

export default class School extends Component {
  state = {
    school: '清华',
    base: '北京'
  }
  render() {
    const {school, base} = this.state
    return (
      <div style={{backgroundColor: 'blue', padding: '10px'}}>
        <h1>这是学校:{school}</h1>
        <Provider value={{school, base}}>
          <Teacher />
        </Provider>
      </div>
    )
  }
}

// pages/Teacher.jsx
import React, { Component } from 'react'
import Students from "./Students"
export default class Teacher extends Component {
  render() {
    return (
      <div style={{backgroundColor: 'yellow', padding: '10px'}}>
        <h2>这是老师</h2>
        <Students />
      </div>
    )
  }
}

// pages/Students.jsx
import React, { Component } from 'react'

export const SchoolContext = React.createContext() // 创建context对象并暴露出去
export const {Provider, Consumer} = SchoolContext // 主要是为了暴露出Provider供祖组件使用

export default function Students() {
    return (
      <div style={{backgroundColor: 'orange', padding: '10px'}}>
        <Consumer> 
          {
            value => { // value相当于this.context,直接取里面的数据即可
              return (<h2>该生来自{value.base}的{value.school}</h2>)
            }
          }
        </Consumer>
      </div>
    )
}

react中组件通信方式主要有如下四种方式:

  1. props
  2. 消息订阅与发布机制,如pubsub-js
  3. 集中式状态管理,如redux
  4. 生产者消费者模式,如conText

错误边界

错误边界(error boundary)用来捕获后代组件在生命周期内产生的错误,从而渲染备用页面;

通常利用getDerivedStateFromError与componentDidCatch(非必需)配合达到效果;两个函数均要定义在父组件中,用于监测容易出错的子组件;

import React, { Component } from 'react'

export default class Parents extends Component {
    state = {hasError: ''} // 表示子组件是否有错
  // 在父组件中定义错误边界,当子组件在生命周期中报错时,会调用该函数
  static getDerivedStateFromError(error) {
    return {hasError: error}
  }
  componentDidCatch() {
    // 该函数可以不写,此处一般用于统计出错次数
  }
  render() {
    return (
      <div>
        <span>这是parents组件</span>
        {/* 判断容易报错的子页面 */}
         {this.state.hasError ? <span>页面出错了。。。(或者此处放一个精心做好的报错页面)</span>: <Son />}
      </div>
    )
  }
}
class Son extends Component {
    state = {}
    render() {
      return (
        <div>
          <span>这是son组件</span>
          <span>{this.state.name.name}</span>
          {/* 请求了一个不存在的数据,会报错 */}
        </div>
      )
    }
  }

children props与render props

之前所有讲过的例子中,三级组件使用都是采用如下方式:

// 祖组件中调用父组件
// 父组件中调用子组件

若想采用在祖组件中直接调用父组件和子组件方式应该采用什么方式呢?以上述讲解context中三个组件为例:

  • 通过props中children标签属性传递
// pages/School.jsx
import React, { Component } from 'react'
import Teacher from './Teacher'
import Students from './Students'

export default class School extends Component {
  state = {
    school: '清华',
    base: '北京'
  }
  render() {
    const {school, base} = this.state
    return (
      <div style={{backgroundColor: 'blue', padding: '10px'}}>
        <h1>这是学校:{school}</h1>
          <Teacher>
            <Students /> // 将子组件作为标签体内容传入
          </Teacher>
      </div>
    )
  }
}

// pages/Teacher.jsx
import React, { Component } from 'react'
export default class Teacher extends Component {
  render() {
    return (
      <div style={{backgroundColor: 'yellow', padding: '10px'}}>
        <h2>这是老师</h2>
        {/* 标签体中的内容通过this.props.children拿到,此时代表Students组件 */}
        {this.props.children} 
      </div>
    )
  }
}

// pages/Students.jsx正常写组件即可

该种方法利用了组件标签标签体中传递的方式,和一般组件不同的是,组件标签的标签体不会被主动渲染,需要在组件中对应位置声明{this.props.children} 进行接收才会展示~

该种方式没法传递数据给子组件,所以引入下面方式:

  • 通过props中render方法

组件标签可以传递render props属性渲染组件,且可以传递参数,相当于在父组件中做了占位,可以任意调用子组件。类似于vue中的插槽方法~

render属性为函数形式,接收传给子组件的参数,一般返回一个组件;在父组件中对应位置调用render方法,并传递给父组件的参数,实现父子间通信;即为{this.props.render(xx参数1,xx参数2,...., xx参数n)}

// pages/School.jsx
import React, { Component } from 'react'
import Teacher from './Teacher'
import Students from './Students'

export default class School extends Component {
  render() {
    return (
      <div style={{backgroundColor: 'blue', padding: '10px'}}>
        <h1>这是学校</h1>
        {/* 通过render属性实现父子组件关系,通过prop传递参数*/}
        <Teacher render={({teacherName, subject}) => <Students teacherName={teacherName} subject={subject}/>} />
      </div>
    )
  }
}

// pages/Teacher.jsx
import React, { Component } from 'react'
export default class Teacher extends Component {
  state = {
    teacherName: '小李',
    subject: '语文'
  } 
  render() {
    const {teacherName, subject} = this.state
    return (
      <div style={{backgroundColor: 'yellow', padding: '10px'}}>
        <h2>这是老师</h2>
        {/* 在展示位置使用{this.props.render渲染子组件} */}
        {this.props.render({teacherName, subject})}
      </div>
    )
  }
}

// pages/Students.jsx
import { Component } from "react"
export default class Students extends Component {
    render() {
      return (
        <div style={{backgroundColor: 'orange', padding: '10px'}}>
         {/* 利用props接收参数 */}
         <h2>该生的{this.props.subject}老师是{this.props.teacherName}</h2>
        </div>
      )
    }
}



做笔记是真不容易啊(灬ꈍ ꈍ灬)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/367.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTTP 3.0来了,UDP取代TCP成为基础协议,TCP究竟输在哪里?

TCP 是 Internet 上使用和部署最广泛的协议之一&#xff0c;多年来一直被视为网络基石&#xff0c;随着HTTP/3正式被标准化&#xff0c;QUIC协议成功“上位”&#xff0c;UDP“取代”TCP成为基础协议&#xff0c;TCP究竟“输”在哪里&#xff1f; HTTP/3 采用了谷歌多年探索的基…

< CSS小技巧:那些不常用,却很惊艳的CSS属性 >

文章目录&#x1f449; 前言&#x1f449; 一. background-clip: text - 限制背景显示&#xff08;裁剪&#xff09;&#x1f449; 二. user-select - 控制用户能否选中文本&#x1f449; 三. :focus-within 伪类&#x1f449; 四. gap - 网格 / 弹性布局间隔设置&#x1f449;…

【C++笔试强训】第三十一天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 选择题 &#x…

Golang每日一练(leetDay0005)

目录 13. 罗马数字转整数 Roman to Integer ★ 14. 最长公共前缀 Longest Common Prefix ★ 15. 三数之和 3Sum ★★★ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 13. 罗马数字转…

我从功能测试到python接口自动化测试涨到22k,谁知道我经历了什么......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 常见的接口&#xf…

C++高并发内存池的设计和实现

一、整体设计 1、需求分析 池化技术是计算机中的一种设计模式&#xff0c;内存池是常见的池化技术之一&#xff0c;它能够有效的提高内存的申请和释放效率以及内存碎片等问题&#xff0c;但是传统的内存池也存在一定的缺陷&#xff0c;高并发内存池相对于普通的内存池它有自己…

卷王都在偷偷准备金三银四了...

年终奖没发&#xff1b; 简历石沉大海&#xff1b; 发消息只读不回 打开某招聘&#xff0c;看了看岗位&#xff0c;这个厂还不错&#xff0c;可是要求好高&#xff0c;我啥都不会。 “哎&#xff0c;算了&#xff0c;我简历还没更新呢&#xff0c;我躺到6月份拿到年终奖再跑…

3-1 SpringCloud快速开发入门: Ribbon 是什么

接上一章节Eureka 服务注册中心自我保护机制&#xff0c;这里讲讲Ribbon 是什么 Ribbon 是什么 通常说的负载均衡是指将一个请求均匀地分摊到不同的节点单元上执行&#xff0c;负载均和分为硬件负载均衡和软件负载均衡&#xff1a; **硬件负载均衡&#xff1a;**比如 F5、深信…

记第一次面试的过程(C++)

说实话三月份上旬过得很充实&#xff0c;而且感觉蛮值&#xff0c;但还有不足的地方&#xff0c;今晚特地看完资料分析来复盘复盘。 时间还要回到3.2中午13.35&#xff08;别问我为什么那么准确&#xff0c;刚刚掏手机看的&#xff09;&#xff0c;我正在吃着饭看着王者荣耀的直…

STL sort 分析

前言 STL 中提供了很多算法&#xff0c;sort 是我们经常使用的&#xff0c;那它究竟是如何实现的呢&#xff1f; STL 的 sort 算法&#xff0c;数据量大时采用快速排序&#xff0c;分段递归。一旦分段的数据量小于某个门槛&#xff0c;为避免快速排序的递归调用带来过大的额外…

三天吃透计算机网络面试八股文

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

Linux常用命令

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。个人主页&#xff1a;小李会科技的…

C++STL 容器案例 员工分组 实现步骤与代码分析与展示 实现步骤的注意事项

STL容器 员工分组案例 文章目录STL容器 员工分组案例1 案例描述2 实现步骤3 案例代码与分析1 案例描述 公司今天招聘了10个员工&#xff08;ABCDEFGHIJ&#xff09;&#xff0c;10名员工进入公司之后&#xff0c;需要指派员工在哪个部门工作员工信息有: 姓名 工资组成&#xf…

CANoe中使用CAPL刷写流程详解(Trace图解)(CAN总线)

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

史上最全最详细的Java架构师成长路径图,程序员必备

从新手码农到高级架构师&#xff0c;要经过几步&#xff1f;要多努力&#xff0c;才能成为为人倚重的技术专家&#xff1f;本文将为你带来一张程序员发展路径图&#xff0c;但你需要知道的是&#xff0c;天下没有普适的道理&#xff0c;具体问题还需具体分析&#xff0c;实践才…

Verilog实现组合逻辑电路

在verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。组合逻辑电路比较简单&#xff0c;仅由基本逻辑门组成---如与门、或门和非门等。当电路的输入发生变化时&#xff0c;输出几乎&#xff08;信号在电路中传递时会有一小段延迟&#xff09;立即就发生…

马上要面试了,还有八股文没理解?让ChatGPT来给你讲讲吧——如何更好使用ChatGPT?

最近这段时间 ChatGPT 掀起了一阵 AI 热潮&#xff0c;目前来看网上大部分内容都是在调戏 AI&#xff0c;很少有人写如何用 ChatGPT 做正事儿。 作为一个大部分知识都是从搜索引擎和 GitHub 学来的程序员&#xff0c;第一次和 ChatGPT 促膝长谈后&#xff0c;基本认定了一个事…

AI又进化了,突破性革命来了

大家好&#xff0c;我是 Jack。 2023 年&#xff0c;AI 真的杀疯了。短短不到一年的时间&#xff0c;当我们还在感慨 AI 一键生成的二次元画作精美万分的时候&#xff0c;它已经进化到了写实美照也能手到擒来的地步。 更多的效果&#xff0c;可以看刚刚发布的视频&#xff0c;…

爽,我终于掌握了selenium图片滑块验证码

因为种种原因没能实现愿景的目标&#xff0c;在这里记录一下中间结果&#xff0c;也算是一个收场吧。这篇文章主要是用selenium解决滑块验证码的个别案列。 思路&#xff1a; 用selenium打开浏览器指定网站 将残缺块图片和背景图片下载到本地 对比两张图片的相似地方&#x…

十大经典排序算法(上)

目录 1.1冒泡排序 1. 算法步骤 3.什么时候最快 4. 什么时候最慢 5.代码实现 1.2选择排序 1. 算法步骤 2. 动图演示 3.代码实现 1.3 插入排序 1. 算法步骤 2. 动图演示 3. 算法实现 1.4 希尔排序 1. 算法步骤 2. 动图演示 3.代码实现 1.5 归并排序 1. 算法步骤 2…
最新文章