React重点知识拓展,含Hooks、路由懒加载等

2023-11-18

第7章:React扩展

一、setState

1.setState更新状态的2种写法

  • setState(stateChange, [callback])------对象式的setState

    • stateChange为状态改变的对象(该对象可以体现出状态的更改)
    • callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
  • setState(updater, [callback])------函数式的setState

    • updater为一个函数,返回stateChange对象。
    • updater可以接收到state和props
    • callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。

2. 总结

  • 对象式的setState是函数式的setState的简写方式(语法糖)

  • 使用原则:

    • 如果新状态不依赖于原状态 ===> 使用对象方式
    • 如果新状态依赖于原状态 ===> 使用函数方式
    • 如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取
  • setState更新状态的动作是异步的

    我们会考虑在 setState 更新之后 log 一下

    add = () => {
     const { count } = this.state
     this.setState({
         count: count + 1
     })
     console.log(this.state.count);
    }
    

    因此可能会写出这样的代码,看起来很合理,在调用完 setState 之后,输出 count

    我们发现显示的 count 和我们控制台输出的 count 值是不一样的

    这是因为,我们调用的 setState 是同步事件,但是它的作用是让 React 去更新数据,而 React 不会立即的去更新数据,这是一个异步的任务,因此我们输出的 count 值会是状态更新之前的数据。“React 状态更新是异步的

二、lazyLoad

1. 路由组件的lazyLoad

懒加载在 React 中用的最多的就是路由组件了,页面刷新时,所有的组件都会重新加载,这并不是我们想要的,我们想要实现点击哪个路由链接再加载即可,这样避免了不必要的加载。若不使用路由懒加载技术,我们页面一加载时,所有的路由组件都会被加载。

  • 首先我们需要从 react 库中暴露一个 lazy 函数

    通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包

    import React, { Component ,lazy} from 'react';

  • 然后我们需要更改引入组件的方式

    // import Home from "./Home";
    // import About from "./About";
    const Home = lazy(() => import("./Home"));
    const About = lazy(() => import("./About"));
    
  • 使用Suspense标签包括要显示的路由组件(Route标签),当我们网速慢的时候,路由组件就会有可能加载不出来,页面就会白屏,它需要我们使用fallback属性来指定一个路由组件加载的东西,相对于 loading

    <Suspense fallback={<h1>loading</h1>}>
        <Route path="/home" component={Home}></Route>
        <Route path="/about" component={About}></Route>
    </Suspense>
    
  • 注意:因为 loading 是作为一个兜底的存在,因此 loading 是 必须提前引入的,不能懒加载

三、Hooks

React Hook/Hooks是什么?

  • Hook是React 16.8.0版本增加的新特性/新语法
  • 可以让你在函数组件中使用 state 以及其他的 React 特性

三个常用的Hook

  • State Hook: React.useState()
  • Effect Hook: React.useEffect()
  • Ref Hook: React.useRef()

1. State Hook

State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作

  • 首先我们需要明确一点,由于函数处于严格模式,函数式组件没有自己的 this

  • 语法: const [xxx, setXxx] = React.useState(initValue)

    function Demo() {
        const [count, setCount] = React.useState(0)
        console.log(count, setCount);
        function add() {
            setCount(count + 1)
        }
        return (
            <div>
                <h2>当前求和为:{count}</h2>
                <button onClick={add}>点我加1</button>
            </div>
        )
    }
    export default Demo
    
  • useState()说明:它让函数式组件能够维护自己的 state

    • 接收一个参数,作为初始化 state 的值,useState 的初始值只有第一次有效
    • useState 能够返回一个数组,第一个元素是 state ,第二个是更新 state 的函数
    • 更新状态函数setXxx的两种写法
      • setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
      • setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

2. Effect Hook

在类式组件中,提供了一些声明周期钩子给我们使用,我们可以在组件的特殊时期执行特定的事情,例如 componentDidMount ,能够在组件挂载完成后执行一些东西

在函数式组件中也可以实现,它采用的是 effectHook ,它的语法更加的简单,同时融合了 componentDidMountcomponentDidUpdatacomponentWillUnmount生命周期,极大的方便了我们的开发

  • Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

  • React中的副作用操作:

    • 发ajax请求数据获取
    • 设置订阅 / 启动定时器
    • 手动更改真实DOM
  • (3). 语法和说明:

    useEffect(() => { 
        // 在此可以执行任何带副作用操作
        return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
        }}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
  • 可以把 useEffect Hook 看做如下三个函数的组合

    • componentDidMount()
    • componentDidUpdate()
    • componentWillUnmount()
  • React.useEffect说明

    • 第一个参数是一个回调函数,第二个参数是一个数组
    • 第二个参数不写所有state更新时,执行回调函数
    • 添加第二个参数(数组):数组中对应的state或prop更新时,执行回调函数,和Vue中的watch侦听类似
    • 若为空数组[],相当于componentDidMount,回调函数只会在第一次render()后执行
    • 返回值是一个函数,该函数会在组件被卸载时执行,相当于componentWillUnmount()
    React.useEffect(() => {
      let timer = setInterval(() => {
        setCount((count) => count + 1);
      }, 1000);
    
      // 返回componentWillUnmount
      return () => {
        clearInterval(timer);
      };
    }, []);
    

3. Ref Hook

当我们想要获取标签内的信息时,在类式组件中,我们会采用 ref 的方式来获取。在函数式组件中,我们可以采用也可以采用 ref 但是,我们需要采用 useRef 函数来创建一个 ref 容器,这和 createRef 很类似。

  • 使用useRef函数创建ref容器

    const myRef = useRef()

  • 给要获取的结点打ref标签

    <input type="text" ref={myRef} />

  • 获取input框输入的值

    function show() {
        alert(myRef.current.value)
    }
    
  • 重点是:useRef函数的使用

    • 语法: const refContainer = useRef()
    • 作用:保存标签对象,功能与React.createRef()一样

四、Fragment

  • 我们编写组件的时候每次都需要采用一个 div 标签包裹才能让它正常的编译,但是这样会引发什么问题呢?
    在这里插入图片描述
    它包裹了几层无意义的 div 标签,我们可以采用 Fragment 来解决这个问题

  • 我们需要从 react 中暴露出 Fragment ,将我们所要去掉的div采用 Fragment 标签进行替换,当它解析到 Fragment 标签的时候,就会把Fragment去掉

  • 同时采用空标签,也能实现,但是它不能接收任何值,而 Fragment 能够接收唯一一个 属性key

  • 使用:<Fragment key={id}><Fragment><></>

  • 作用:可以不用必须有一个真实的DOM根标签了

五、Context

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

1. 仅适用于类式组件

  • 首先我们需要全局创建一个 MyContext 对象,我们需要引用MyContext 下的 Provider

    // 创建Context容器对象
    const MyContext = React.createContext();
    const { Provider } = MyContext;
    
  • 渲染子组件时,外面包裹Provider, 通过value属性给后代组件传递数据

    <Provider value={{ username, age }}>
        <B />
    </Provider>
    
  • 在需要使用数据的后代组件,引入MyContext

    // 声明context
    static contextType = MyContext;
    
  • 在使用数据时,直接从 this.context 上取值即可

    const {username,age} = this.context
    

2. 适用于函数式和类式组件

  • 首先我们需要全局创建一个 MyContext 对象,我们需要引用MyContext 下的 ProviderConsumer

    // 创建Context容器对象
    const MyContext = React.createContext();
    const { Provider , Consumer } = MyContext;
    
  • 渲染子组件时,还是一样,外面包裹Provider, 通过value属性给后代组件传递数据

  • 在需要使用数据的子组件中,使用<Consumer></Consumer>包裹,通过 value 取值即可

    function C() {
      return (
        <div>
          <h3>我是C组件,我从A接收到的数据 </h3>
          <Consumer>
            {(value) => {
              return `${value.username},年龄是${value.age}`;
            }}
          </Consumer>
        </div>
      );
    }
    

3. 注意

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

六、组件优化

1. Component的2个问题

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

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

2. 效率高的做法

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

3. 原因

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

4. 解决

  • 办法1:

    • 重写shouldComponentUpdate()方法​

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

    • 如下代码

      shouldComponentUpdate(nextProps, nextState) {
          // console.log(this.props, this.state); // 目前的props和state
          // console.log(nextProps, nextState); // 接下来要变化的目标props,目标state
          return !(this.state.carName === nextState.carName);
      }
      
  • 办法2:

    • 使用PureComponent

      react 身上暴露出 PureComponent 而不使用 Component

      import React, { PureComponent } from 'react'
      
    • PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true

    • 注意:

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

        • 错误示例:

          // stus为数组
          stus.unshift("小刘");
          this.setState({ stus });
          
          const obj = this.state;
          obj.carName = "迈巴赫";
          console.log(obj === this.state); // true,判断时认为数据未变化,不更新页面
          this.setState(obj);
          
      • 不要直接修改state数据, 而是要产生新数据

    • 项目中一般使用PureComponent来优化

七、render props

采用 render props 技术,我们可以像组件内部动态传入带有内容的结构,和Vue插槽类似

1. 组件标签内填入内容时

  • 当我们在一个组件标签中填写内容时,这个内容会被定义为 children props,我们可以通过 this.props.children 来获取

  • 如:

    <A>hello</A>
    
  • 这个 hello 我们就可以通过 children 来获取

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

  • Vue中:
    • 使用slot技术, 也就是通过组件标签体传入结构 <AA><BB/></AA>
  • React中:
    • 使用children props: 通过组件标签体传入结构​
    • 使用render props: 通过组件标签属性传入结构, 一般用render函数属性

3. children props

  • 在Parent组件中

    <A>
    	<B>xxxx</B>
    </A>
    
  • A组件中使用B组件,AB组件为父子组件关系

    {this.props.children}

  • 问题: 如果B组件需要A组件内的数据 ==> 做不到

4. render props

  • 在Parent组件中

    <A render={(name) => <B name={name} />} />

  • A组件: {this.props.render(name)}

  • C组件: 读取A组件传入的数据显示{this.props.name}


八、错误边界

1. 理解:

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

2. 特点:

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

3. 使用方式:

getDerivedStateFromError配合componentDidCatch

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

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

九、组件通信方式总结

1. 方式:

  • props:

    • children props

    • render props​

  • 消息订阅-发布:pubs-sub、event等等​

  • 集中式管理:redux、dva等等

  • Context:生产者-消费者模式

2. 选择方式

  • 父子组件:props
  • 兄弟组件(非嵌套组件):消息订阅-发布、集中式管理
  • 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(用的少)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React重点知识拓展,含Hooks、路由懒加载等 的相关文章

随机推荐

  • 通过tableExport.js插件导出jqgrid表格数据

    通过tableExport js插件导出jqgrid表格数据 提前说 没有实现导出pdf png和ppt 实现了导出 JSON XML CSV TXT SQL MS Word Ms Excel 代码实例 包含改进的tableExport j
  • MySQL建表设置默认值取值范围

    一 设置默认值 设置默认值采用default 如代码所示 二 设置取值范围 设置取值范围采用check 如代码所示 create table student id int not null primary key auto incremen
  • java项目自动化单元测试

    对于我们开发人员来说 单元测试一定不会陌生 但在各种原因下会被忽视 尤其是在我接触到的项目中 提测阶段发现各种各样的问题 我觉得有必要聊一下单元测试 为了写而写的单元测试没什么价值 但一个好的单元测试带来的收益是非常客观的 问题是怎么去写好
  • CVAT标注工具的部署步骤详解

    简介 CVAT Computer Vision Annotation Tool 此标注工具是用于机器视觉数据标注的在线标注工具 以网页形式标注 能够生成多种数据标注格式基本涵盖了市面上百分之九十以上格式 此工具也有自己的标注格式 此工具的优
  • Canvas 详解

    HTML 5 Canvas 参考手册
  • ES6语法知识点

    目录 let const 常用 暂时性死区 const 建议 箭头函数 常用 建议 iterator迭代器 解构赋值 常用 建议 剩余 扩展运算符 常用 扩展运算符 剩余运算符 在对象中使用扩展运算符 建议 对象属性 方法简写 常用 对象属
  • centos7 搭建Hadoop3.0.3完全分布式

    第一步 服务器规划 IP地址 主机名称 nameNode dataNode 192 168 60 201 master 是 否 192 168 60 200 node1 否 是 第二步 基于依赖环境准备 1 centos7 搭建JDK8 参
  • Java后台面试题

    Java后台面试题 一 Java内存 私有内存区 伴随线程的产生而产生 一旦线程终止 私有内存区也会自动消除 程序计数器 指示当前程序执行到了哪一行 执行Java方法时记录正在执行的虚拟机字节码指令地址 执行本地方法时 计数器值为null
  • Linq语法详细

    1 简单的linq语法 1 var ss from r in db Am recProScheme select r 2 var ss1 db Am recProScheme 3 string sssql select from Am re
  • 不加电透明屏:在场景化应用中,有哪些特点和优点?

    不加电透明屏是一种新型的显示技术 它可以在不需要电源的情况下显示图像和文字 这种屏幕的原理是利用光的折射和反射来实现显示效果 而不需要通过电流来激发像素点 不加电透明屏的最大优点是节能环保 传统的显示屏需要消耗大量的电能来显示图像 而不加电
  • 环境搭建04-Ubuntu16.04更改conda,pip的镜像源

    我常用的pipy国内镜像源 https pypi tuna tsinghua edu cn simple 清华 http mirrors aliyun com pypi simple 阿里云 https pypi mirrors ustc
  • Java-基于SSM的药品销售管理系统

    项目背景 本论文主要论述了如何使用JAVA语言开发一个药品销售系统 本系统将严格按照软件开发流程进行各个阶段的工作 采用B S架构 面向对象编程思想进行项目开发 在引言中 作者将论述药品销售系统的当前背景以及系统开发的目的 后续章节将严格按
  • 【论文翻译】基于层次结构的动态异构图嵌入

    基于层次结构的动态异构图嵌入 Dynamic Heterogeneous Graph Embedding Using Hierarchical Attentions 百度学术 摘要 图嵌入已经引起了许多研究兴趣 现有的研究主要集中在静态同质
  • 2018.08.31 WorkSummary——05

    最近在做一个SMH spring springmvc hibernate 的项目 比较有意思 主要是在前端做大数据展示 后台业务较少 但是表特别多 一个图对应一个表 一共上百个图 hibernate是特点是操作对象等于操作数据库 每个表对应
  • React Native

    小手动一动 点赞转发加关注 微信搜索 大前端杂货铺 公众号关注大前端老司机带您遨游大前端知识的海洋 关注 Github https github com big frontend 还有大前端代码实践哦 java 与 javascript 互
  • C++基础——简单而强大的bitset

    basis bitset 的构造 bitset的操作 一些高级用法 将Bitsets视为一组标志 一些简单的原子操作 往往能组合出复杂而强大的功能 位操作的深远意义不在于表示一种数值 而是可能的情况数 我虽然暂时不知道bitset能组合出如
  • Python 之列表添加元素的3种方法

    一 追加单个值 append 方法 追加单个元素 gt gt gt list crazyit 20 2 gt gt gt list append fkit gt gt gt print list crazyit 20 2 fkit 二 追加
  • Configure and build Mesa3D

    1 环境 Mesa3D 21 1 4 Mesa3D demos Ubuntu 20 04 2 配置环境 sudo apt install gcc sudo apt install g sudo apt install vim sudo ap
  • React-Native ERROR: JAVA_HOME is not set and no ‘java’ command could be found in your PATH.

    ERROR JAVA HOME is not set and no java command could be found in your PATH Please set the JAVA HOME variable in your env
  • React重点知识拓展,含Hooks、路由懒加载等

    第7章 React扩展 一 setState 1 setState更新状态的2种写法 setState stateChange callback 对象式的setState stateChange为状态改变的对象 该对象可以体现出状态的更改