个人技术分享

react笔记-扩展篇

包括新用法和hooks

1. setState

setState更新状态的两种写法:

1. setState({}, [() => {}])
2. setState(() => {
  // 返回一个状态修改对象
  return {}
}, [() => {}])

1. 对象写法:

⚠️注意:react更新状态是异步的

import React, { Component } from 'react'

export default class Demo extends Component {
    state = {
        count: 0
    }

    add = () => {
        // 更新状态:react更新状态是异步的
        this.setState({
            count: this.state.count + 1
        }, () => {
          	// 该回调的可以获取到state更新后的结果,可选参数
            console.log(this.state.count); // 1 更改后
        })
        console.log(this.state.count); // 0 更改前
    }

    render() {
        return (
            <div>
                <p>{this.state.count}</p>
                <button onClick={this.add}>+1</button>
            </div>
        )
    }
}

2. 函数写法:

回调参数可以收到stateprops

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

总结:

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

2. lazyLoad

路由组件的lazyLoad:懒加载

import React, { Component, lazy, Suspense } from 'react'
import { Route } from 'react-router-dom'

const Home = lazy(() => import('./Home'))

export default class Demo extends Component {
    render() {
        return (
            <div>
            		{/* Suspense 包裹路由组件, 需要给定fallback */}
                <Suspense fallback={<div>loading...</div>}>
                    <Route path='/home' component={Home} />
                </Suspense>
            </div>
        )
    }
}

3. Hooks

3.1 Hooks是什么

hooks是react16.8.0新增的新特性,可以在函数式组件中使用state以及其他react新特性。

3.2 三个常用的hook

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

3.3 useState

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

import React from 'react'

function Demo() {
    const [count, setCount] = React.useState(0)

    function change() {
      	// 第一种写法:参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
        setCount(count + 1)
      	// 第二种写法:参数为函数,接收原来的状态值,返回新的状态值,内部用其覆盖原来的状态值
        setCount((num) => {
            return num + 1
        })
    }

    return <>
        <div>{count}</div>
        <button onClick={change}>+1</button>
    </>
}

export default Demo

通过import { useState } from 'react'也可以

3.4 useEffect

import { useState, useEffect } from 'react'

function Demo() {
    const [count, setCount] = useState(0)

    // 	情况1:只给第一个参数:每当数据更改一次,调用一次回调
    useEffect(() => {
        // 可以执行任何副作用操作
      	// 例如:网络请求,定时器
        console.log('数据更改了');
    })

    // 情况2:如果第一个参数返回一个函数,返回的函数可以做一些收尾工作,例如:清除定时器
    useEffect(() => {
        console.log('数据更改了');
        return () => {
            console.log('组件销毁了');
        }
    })

    // 情况3:监听count的改变,count改变了,就会调用回调
    useEffect(() => {
        console.log('数据更改了');
    }, [count, ...])

    // 情况4:参数2为空数组,谁都不监听,回调只有第一次render才会被调用
    useEffect(() => {
        console.log('数据更改了');
    }, [])

    return <>
        <div>{count}</div>
        <button onClick={() => { setCount(count + 1) }}>+1</button>
    </>
}

export default Demo

可以把useEffect看作三个函数的组合:

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

3.5 useRef

import { useRef } from 'react'

function Demo() {
    let myRef = useRef()

    function getRef() {
        console.log(myRef.current.value)
    }

    return <>
        <input type="text" ref={myRef} />
        <button onClick={getRef}>ref</button>
    </>
}

export default Demo

4. Fragment

类似于vue的template,不会解析到页面上

<Fragment></Fragment>
<></>
import { Fragment } from 'react'

function Demo() {
  	// Fragment可以绑定key,循环的时候可以传递key,空标签不允许传任何属性
    return <Fragment key={id}>
        hello
    </Fragment>
}

export default Demo

5. Context

Context:一种组件间的通信方式。常用于【祖组件】与【后代组件】间的通信。

使用:

  1. 创建Context容器对象:
const MyContext = createContext()
  1. 渲染子组件时,外层包裹XxxContext.Provider,通过value属性给后代组件传递数据
export default class A extends Component {
    render() {
        return (
            <div>
                A
                <MyContext.Provider value={{ name: 'xiaotian', age: 18 }}>
                    <B />
                </MyContext.Provider>
            </div>
        )
    }
}

也可以解构Provider:

const { Provider } = MyContext;

<Provider></Provider>
  1. 后代组件读取数据(两种方式):

    1. 第一种方式:仅适用于类式组件
    class C extends Component {
      	// 声明接收context,通过this.context读取value的数据
        static contextType = MyContext
      
        render() {
            return (
                <div>
                    C
                    {this.context.name}
                </div>
            )
        }
    }
    
    1. 第二种方式:函数式和类式组件都行
    <XxxContext.Consumer>
        {
            (value) => {
                // value就是context中value的数据
              	return (需要返回的内容)
            }
        }
    </XxxContext.Consumer>
    

    例子:

    // C组件
    function C() {
        return (
            <MyContext.Consumer>
                {
                    (value) => {
                        return (
                            <div>
                                C
                                {value.name}
                            </div>
                        )
                    }
                }
            </MyContext.Consumer>
        )
    }
    

    6. 组件优化

    6.1 import {Component} from 'react'的Component的两个问题:

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

    6.2 效率高的做法:

    只有当组件的stateprops发生改变的时候才重新render

    6.3 原因:

    Component中的shouldComponentUpdate()总是返回true

    6.4 解决方法:

    类式组件

    1. 方法1:重写shouldComponentUpdate()方法,比较新旧stateprops数据,只有发生变化才返回true,否则返回false

    2. 方法2:使用PureComponentPureComponent重写了shouldComponentUpdate(),只有当stateprops发生变化时才返回true。

      import { PureComponent } from 'react'
      

      ⚠️注意:只是进行stateprops数据的浅比较,如果只是数据对象内部数据变了,返回的是false。

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

    函数式组件:可以通过使用React.memo来实现类似PureComponent的优化效果。下面是关于React.memo的基本使用方法:

    1. 基本用途React.memo是一个高阶函数,它接收一个函数组件作为参数,并返回一个新的经过优化的组件,该组件在props没有变化时避免不必要的渲染。
    2. 用法示例
    import React, { memo } from 'react'
    
    const MyFunctionalComponent = (props) => {
     // 组件逻辑...
     return <div>{props.text}</div>
    }
    
    export default memo(MyFunctionalComponent)
    

    在这个例子中,MyFunctionalComponentmemo包裹,这意味着React会跳过重新渲染该组件,除非它的props发生了变化。

    1. 深入理解:与PureComponent相似,React.memo执行浅比较来检查props是否变化。如果props是复杂对象且需要深层比较,你可以传递第二个参数,即一个自定义的比较函数来进行更精细的控制。
    2. 注意事项React.memo只对props的变化做出反应,组件内部的state或context变化依然会导致重渲染。此外,如果组件有副作用(如数据获取、订阅等),应该在组件内部使用适当的生命周期方法或Hooks来管理这些副作用。

    通过这种方式,React.memo帮助提高了函数式组件的性能,减少了不必要的渲染,特别是在大型应用程序中,这种优化可以带来显著的性能提升。

7. renderProps(插槽技术)

类似于vue中的插槽

import React, { Component } from 'react'

export default class Demo extends Component {
    render() {
        return (
            <div>
            		{/* <BComponent name={name} /> 可以渲染任何组件 */}
                <AComponent render={(name) => <BComponent name={name} />} />
            </div>
        )
    }
}

// 父组件
class AComponent extends Component {
    render() {
        return (
            <div>
                <div>A组件</div>
            		{/* 预留的位置,类似于vue中的插槽 */}
                { this.props.render('xiaotian') }
            </div>
        )
    }
}

// 子组件
class BComponent extends Component {
    render() {
        return (
            <div>B组件---A组件的name为:{this.props.name}</div>
        )
    }
}

8. 错误边界

8.1 理解:

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

8.2 特点:

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

8.3 使用:

// 当子组件报错时,会触发getDerivedStateFromError,并携带错误信息
static getDerivedStateFromError(error) {
    console.log(error);
    return { hasError: true }
}

// 当渲染组件时出错会调用该钩子,用于统计错误信息,反馈给服务器
componentDidCatch(error, errorInfo) {
    console.log(error, errorInfo);
}
import React, { Component } from 'react'

export default class Parent extends Component {
    state = {
        hasError: '' // 用于标识子组件是否产生错误
    }

    // 当子组件报错时,会触发getDerivedStateFromError,并携带错误信息
    static getDerivedStateFromError(error) {
        console.log(error);
        return { hasError: error }
    }

    // 当渲染组件时出错会调用该钩子,用于统计错误信息,反馈给服务器
    componentDidCatch(error, errorInfo) {
        console.log(error, errorInfo);
    }

    render() {
        return (
            <div>
                { this.state.hasError ? '当前网络不稳定' : <Child /> }
            </div>
        )
    }
}

class Child extends Component {
    state = {
        // list: [1, 2, 3, 4, 5] 正常情况
        list: 'abc' // 错误情况
    }
    render() {
        return (
            <ul>
                {
                    this.state.list.map((item, index) => {
                        return <li key={index}>{item}</li>
                    })
                }
            </ul>
        )
    }
}