Young Kbt blog Young Kbt blog
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)

Shp Liu

朝圣的使徒,正在走向编程的至高殿堂!
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)
  • 超文本标记语言 - Html

  • 解释编程语言 - JavaScript

  • JS 超集语言 - TypeScript

  • 界面构建框架 - React

    • React - 基础与核心
    • React - 脚手架及使用
    • React - 路由
    • React - Redux
    • React - 函数类型
    • React - 进阶知识
      • setState函数式状态
        • setState更新状态的2种写法
        • 总结
      • lazyLoad懒加载和Suspense
        • lazyLoad
        • Suspense
      • Hooks函数式
        • React Hook/Hooks是什么?
        • 三个常用的Hook
        • State Hook
        • Effect Hook
        • Ref Hook
      • Fragment标签碎片
        • 背景
        • 使用
        • 作用
      • Context通信
        • 理解
        • 使用
        • 注意
      • PureComponent组件优化
        • Component目前的2个问题
        • 效率高的做法
        • 原因
        • 解决
      • render props传入标签内容
        • 如何向组件内部动态传入带内容的结构(标签)?
        • children props
        • render props
      • 错误边界
        • 理解
        • 特点
        • 使用方式
      • 组件通信方式总结
        • 组件间的关系
        • 几种通信方式
        • 比较好的搭配方式
    • React - 父子传值与循环key
  • 渐进式框架 - Vue2

  • 渐进式框架 - Vue3

  • 前端
  • 界面构建框架 - React
Young Kbt
2022-02-21
目录

React - 进阶知识

  • setState函数式状态
    • setState更新状态的2种写法
    • 总结
  • lazyLoad懒加载和Suspense
    • lazyLoad
    • Suspense
  • Hooks函数式
    • React Hook/Hooks是什么?
    • 三个常用的Hook
    • State Hook
    • Effect Hook
    • Ref Hook
  • Fragment标签碎片
    • 背景
    • 使用
    • 作用
  • Context通信
    • 理解
    • 使用
    • 注意
  • PureComponent组件优化
    • Component目前的2个问题
    • 效率高的做法
    • 原因
    • 解决
  • render props传入标签内容
    • 如何向组件内部动态传入带内容的结构(标签)?
    • children props
    • render props
  • 错误边界
    • 理解
    • 特点
    • 使用方式
  • 组件通信方式总结
    • 组件间的关系
    • 几种通信方式
    • 比较好的搭配方式

# setState函数式状态

# setState更新状态的2种写法

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

    stateChange 为状态改变对象(该对象可以体现出状态的更改)

    callback 是可选的回调函数,它在状态更新完毕、界面也更新后(render 调用后)才被调用

    const {count} = this.state
    // 更新状态
    this.setState({count:count+1},()=>{
        console.log(this.state.count);  // 打印出来时 更新后的数据
    })
    // console.log('12行的输出',this.state.count);  // 异步,所以打印出来的 count 是没 +1 之前的数据
    
    1
    2
    3
    4
    5
    6
  • setState(updater, [callback]) 函数式的 setState

    updater 为返回 stateChange 对象的函数。

    updater 可以接收到 state 和 props。

    callback 是可选的回调函数,它在状态更新、界面也更新后(render 调用后)才被调用。

# 总结

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

使用原则:

  • 如果新状态不依赖于原状态 ===> 使用对象方式

  • 如果新状态依赖于原状态 ===> 使用函数方式

  • 如果需要在 setState() 执行后获取最新的状态数据,要在第二个 callback 函数中读取

this.setState( (state,props) => ({count:state.count+1}),()=>{})
1

# lazyLoad懒加载和Suspense

# lazyLoad

懒加载作用:初始页面不会全部加载懒加载包裹的组件,当点击组件时候再去加载,第二次再点击相同的懒加载包裹的组件就没有用了。

具体:

import { lazy } from 'react'

// 不使用lazy,原先的import
// import xxx from '组件'

// 使用lazy替换原来的import
const xxx = lazy(function(){
    import('组件') 
})
1
2
3
4
5
6
7
8
9

# Suspense

作用:Suspense 包括的组件如果还在渲染或者渲染失败的时候,显示自定义组件(Loading,404 等)

具体:<Suspense fallback={自定义组件}> 组件内容 </Suspense>

import React, { Component,lazy,Suspense} from 'react'
// 1.通过 React 的 lazy 函数配合 import() 函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Login = lazy(()=>import('@/pages/Login'))
......
// 2.通过 <Suspense> 指定在加载得到路由打包文件前显示一个自定义 loading 界面
render() {
    <Suspense fallback={<h1>loading.....</h1>}>
        <Switch>
            <Route path="/xxx" component={Xxx}/>
            <Redirect to="/login"/>
        </Switch>
    </Suspense>
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# Hooks函数式

# React Hook/Hooks是什么?

  1. Hook 是 React 16.8.0 版本增加的新特性/新语法
  2. 可以让你在 函数组件中使用 state 、生命周期函数、ref 以及其他的 React 特性

# 三个常用的Hook

  1. State Hook: React.useState()

  2. Effect Hook: React.useEffect()

  3. Ref Hook: React.useRef()

# State Hook

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

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

  3. useState() 说明:

    • 参数:第一次初始化指定的值在内部作缓存,即初始化的 state 的 value 值

    • 返回值: 包含 2 个元素的数组,第 1 个为内部当前 state 状态 key,第 2 个为更新状态值的函数

  4. setXxx() 的 2 种写法:

    setXxx(newValue): 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值

    setXxx(value => newValue): 参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值

下列需求:初始化 count 值为 0,点击一次加 1

import React from 'react'
function Demo(){
	const [count,setCount] = React.useState(0)   // 初始化 count=0,存入 state 里
    add(){
        // 第一种
        // setXxx(count+1)
        // 第二种
        setCount(count => count+1 )
    }
    return (
    	<div>
			<h2>当前求和为:{count}</h2>
			<button onClick={add}>点我+1</button>
		</div>
    )
 }
export default Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# Effect Hook

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

  2. React 中的副作用操作:

    • 发 ajax 请求数据获取

    • 设置订阅 / 启动定时器

    • 手动更改真实 DOM

  3. 语法和说明: (useEffect 接收两个参数)

    • 参数一:为函数,如果没有第二个参数,则代表 componentDidMount 和 componentDidUpdate两个生命函数

    • 该函数可以有返回值,返回值为函数,且该函数其实就是componentWillUnmount 生命函数

    • 参数二:为数组,可指定监测哪个 state 里的数据

      比如监测 state 的 count 和 name 属性,则 ['count','name](通过 componentDidUpdate 生命函数)

      数组为空,则代表所有数据都不监听,即只有 componentDidMount 生命函数被调用

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

    componentDidMount()
    componentDidUpdate()
    componentWillUnmount() 
    
    1
    2
    3

需求:页面挂载完,定时器让 count 每秒 +1,销毁前清除定时器

import React from 'react'
import ReactDOM from 'react-dom'
function Demo(){
	const [count,setCount] = React.useState(0)
	React.useEffect(()=>{
		let timer = setInterval(()=>{
			setCount(count => count+1 )
		},1000)  // 页面一挂载就执行该操作,componentDidMount生命函数
		return ()=>{// 页面即将卸载实行该操作,componentWillUnmount生命函数
			clearInterval(timer)
		} 
	},[]) // [] 代表不使用 componentDidUpdate 生命函数,去掉 [] 代表 componentDidMount 生命函数和 componentDidUpdate 生命函数都使用

	// 加的回调
	function add(){
		// setCount(count+1) //第一种写法
		setCount(count => count+1 )
	}
	// 卸载组件的回调
	function unmount(){
		ReactDOM.unmountComponentAtNode(document.getElementById('root'))
	}
	return (
		<div>
			<h2>当前求和为:{count}</h2>
			<button onClick={add}>点我+1</button>
			<button onClick={unmount}>卸载组件</button>
		</div>
	)
}

export default Demo
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

# Ref Hook

  1. Ref Hook 可以在函数组件中存储 / 查找组件内的标签或任意其它数据
  2. 语法:const refContainer = React.useRef(),和const refContainer = React.createRef() 类似
  3. 作用:保存标签对象,功能与 React.createRef() 一样
import React from 'react'
function Demo(){
    const myRef = React.useRef()
    function show(){
        alert(myRef.current.value)
    }
    return (
        <div>
            <input type="text" ref={myRef}/>
            <button onClick={show}>点我提示数据</button>
        </div>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# Fragment标签碎片

# 背景

render(){return()} 的 return 里必须有一个根标签包裹所有标签,渲染时候这个根标签也会被渲染,如果不想根标签被渲染,使用 Fragment 代替。

# 使用

<Fragment key={xx}>  { /* 只接受 key,可写,可不写,遍历数组需要些 */ }
    // 真正需要的标签们
</Fragment>

// 另一种写法,空标签
<>
// 真正需要的标签们
<>
1
2
3
4
5
6
7
8

# 作用

可以不用必须有一个真实的 DOM 根标签了,即 Fragment 可以代替一个 div,且渲染时候 Fragment 会被去掉。

# Context通信

# 理解

一种组件间通信方式,常用于【祖组件】与【后代组件】间通信,和 Vue 的 provide 和 inject 一样。

# 使用

  1. 创建 Context 容器对象:(在类组件或者函数组件外面定义)

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

    <xxxContext.Provider value={数据}>
     // 子组件
     </xxxContext.Provider>
    
    1
    2
    3
  3. 后代组件读取数据:

    第一种方式:仅适用于类组件

    static contextType = xxxContext  // 声明接收 context
    this.context // 读取 context 中的 value 数据
    
    1
    2

    第二种方式:函数组件与类组件都可以

    <xxxContext.Consumer>
        {
            value => ( // value 就是 context 中的 value 数据
                // 要显示的内容
            )
        }
    </xxxContext.Consumer>
    
    1
    2
    3
    4
    5
    6
    7

# 注意

在应用开发中一般不用 Context,一般都用它的封装 React 插件。

# PureComponent组件优化

# Component目前的2个问题

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

  2. 只当前组件重新 render(),就会自动重新 render 子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

# 效率高的做法

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

# 原因

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

# 解决

  1. 办法 1:重写 shouldComponentUpdate() 方法(手写)

    • 比较新旧 state 或 props 数据,如果有变化才返回 true,如果没有返回 false
    shouldComponentUpdate(nextProps,nextState){
        return !this.state.carName === nextState.carName
    }
    
    1
    2
    3
  2. 办法 2:使用 PureComponent(官方封装)

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

    注意:

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

    • 不要直接修改 state 数据,而是要产生新数据(使用新数据替换原来数据,而不是原来数据的修改)

    项目中一般使用 PureComponent 来优化

    import React, { PureComponent } from 'react'
    // 原先
    export default class Parent extends Component {}
    // 使用 PureComponent
    export default class Parent extends PureComponent {}
    
    1
    2
    3
    4
    5

# render props传入标签内容

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

  1. Vue 中:

    使用 Slot 技术,也就是通过组件标签体传入结构 <A><B/></A>

  2. React 中:

    使用 children props:通过组件标签体传入结构

    使用 render props:通过组件标签属性传入结构,而且可以携带数据,一般用 render 函数属性

# children props

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

# render props

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

整体代码:(A 组件和 B 组件是父子关系,但是 A 组件内不写 B 组件,而是通过父组件 Parent 给 A 和 B 进行父子组件绑定,需要 A 组件把数据传给父组件 Parent,父组件 Parent 再把数据传给 B 组件)

import React, { Component } from 'react'
import C from '../1_setState'
export default class Parent extends Component { // 父组件 Parent
    render() {
        return (
            <div className="parent">
                <h3>我是Parent组件</h3>
                <A render={(name)=><C name={name}/>}/>
            </div>
        )
    }
}
class A extends Component {  // 子组件 A
    state = {name:'tom'}
    render() {
        const {name} = this.state
        return (
            <div className="a">
                <h3>我是A组件</h3>
                {this.props.render(name)}
            </div>
        )
    }
}
class B extends Component {  //子组件 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

# 错误边界

# 理解

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面,解决后代组件出现错误,导致整个组件都出现错误扩散。

# 特点

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

# 使用方式

getDerivedStateFromError 配合 componentDidCatch,前者进行错误捕获,后者进行错误统计。

错误捕获:然后修改管理错误的数据,一旦错误的数据进行更新,说明出现错误,则出现错误的组件不再显示,显示自定义组件(使用三目运算进行判断)。

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

componentDidCatch(error, info) {
    // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}
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

# 组件通信方式总结

# 组件间的关系

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

# 几种通信方式

props:

  • children props
  • render props

消息订阅-发布:

  • pubs-sub、event 等等

集中式管理:

  • redux、dva 等等

conText:

  • 生产者-消费者模式

# 比较好的搭配方式

  • 父子组件:props
  • 兄弟组件:消息订阅-发布、集中式管理
  • 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)
编辑此页 (opens new window)
#React
更新时间: 2023/09/18, 16:34:13
React - 函数类型
React - 父子传值与循环key

← React - 函数类型 React - 父子传值与循环key→

最近更新
01
技术随笔 - Element Plus 修改包名 原创
11-02
02
Reactor - 扩展性
11-02
03
Reactor - 最佳实践
11-02
更多文章>
Theme by Vdoing | Copyright © 2021-2024 Young Kbt | blog
桂ICP备2021009994号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式