知识总结 React【拓展】

67 阅读5分钟

React拓展

setState

对象式

setState(stateChange, [callback]) ------对象式的 setStatesetState 引起的后续动作是异步的。

state = { count: 0 }
add = () => {
    this.setState({count: count + 1})
    console.log(this.state.count) // 这里输出0,因为setState异步操作还没执行
}
  1. stateChange 为状态改变对象(该对象可以体现出状态的更改)。
  2. callback 是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用。
    state = { count: 0 }
    add = () => {
        this.setState({count: count + 1}, () => {
            console.log(this.state.count) // 1
        })
    }
    

函数式

setState(updater, [callback]) ------函数式的 setState

  1. updater 为返回 stateChange 对象的函数。
  2. updater 可以接收到 stateprops
  3. callback 是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
state = { count: 0 }
add = () => {
    this.setState((state, props) => {
        return {count: state.count + 1} // 可以通过形参获取state内的值
    })
}

总结

  1. 对象式的 setState 是函数式的 setState 的简写方式(语法糖)。
  2. 使用原则:
    • 如果新状态不依赖于原状态 ===> 使用对象方式。
    • 如果新状态依赖于原状态 ===> 使用函数方式。
    • 如果需要在 setState() 执行后获取最新的状态数据,要在第二个 callback 函数中读取。

lazyLoad

路由组件无论看与不看都已经都引进来了,可以使用路由懒加载优化。

使用方法:

  1. 引入 lazy 函数

    import {lazy} from 'react'
    
  2. 路由调用 lazy 函数

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

    const Home = lazy(() => import('./Home'))
    
  3. 引入 Suspense 组件

    通过 <Suspense> 指定在加载得到路由打包文件前显示一个自定义 loading 界面。

    import {lazy, Suspense} from 'react'
    
    import Loading from './Loading' // loading组件不能用懒加载了
    
    <Suspense fallback={<Loading />}>
        <Route path="./home" component={Home}></Route>
    </Suspense>
    

hooks

函数式组件在 16.8 版本前没有自己的 this 指向,因此使用不了 stateaction ,因此没有类式组件好用。

是什么

  1. HookReact 16.8.0 版本增加的新特性/新语法。
  2. 可以让你在函数组件中使用 state 以及其他的 React 特性。

State Hook

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

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

useState() 说明:

  • 参数:第一次初始化指定的值在内部作缓存。
  • 返回值:包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数。
import React from 'react'

function Demo() {
    const [count, setCount] = React.useState(0) // 定义state的值,每一次调用都会执行,如果没有count,赋初始值0,如果有,则缓存这个值,覆盖0
    
    add = () => {
        setCount(count + 1)
    }
    
    return (
        <div>{count}</div>
        <button onClick={add}></button>
    )
}

setXxx() 2种写法:

  1. setXxx(newValue): 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值。
  2. setXxx(value => newValue):参数为函数,接收原本的状态值, 返回新的状态值,内部用其覆盖原来的状态值。
    add = () => {
        setCount(count => count + 1)
    }
    

Effect Hook

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

语法和说明:

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

[stateValue] 是一个可选参数,如果都不写,state的值发生变化也会触发。如果设为空数组,谁也不监测,挂载时触发一次。

import React from 'react'

function Demo() {
    React.useEffect(() => {
        console.log('@')
    }, []) // 类似componentDidMount钩子函数,挂载时触发一次

    return (
        <div></div>
    )
}

总结:

可以把 useEffect Hook 看做如下三个函数的组合:

  • componentDidMount() :第二个参数为[]
  • componentDidUpdate() :第二个参数不填
  • componentWillUnmount() :在第一个参数中返回一个函数。

Ref Hook

Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据。

语法:

const refContainer = useRef()

作用:保存标签对象,功能与React.createRef() 一样。

Fragment

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

使用

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

Context

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

使用

  1. 创建Context容器对象:
    const XxxContext = React.createContext()
    
  2. 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
    <xxxContext.Provider value={数据}>
       子组件
    </xxxContext.Provider>
    
  1. 后代组件读取数据:
    //第一种方式:仅适用于类组件 
    static contextType = xxxContext  // 声明接收context
    this.context // 读取context中的value数据 
    
    第二种方式: 函数组件与类组件都可以
    <xxxContext.Consumer>
      {
        value => ( // value就是context中的value数据
          要显示的内容
        )
      }
    </xxxContext.Consumer>
    

注意:

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

Component的2个问题

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

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

解决办法:

  1. 重写 shouldComponentUpdate() 方法

    比较新旧 stateprops 数据, 如果有变化才返回 true , 如果没有返回 false

  2. 使用 PureComponent

    PureComponent 重写了shouldComponentUpdate() ,只有stateprops 数据有变化才返回 true

    import {PureComponent} from 'react'
    
    class Child extends PureComponent {}
    

注意:

  • 只是进行 stateprops 数据的浅比较, 如果只是数据对象内部数据变了, 返回 false
  • 不要直接修改 state 数据, 而是要产生新数据,因为对象的地址没有发生改变。
  • 项目中一般使用 PureComponent 来优化。

render props

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

Vue中:使用slot技术, 也就是通过组件标签体传入结构

<A><B/></A>

React中:

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

    <A>
      <B>xxxx</B>
    </A>
    {this.props.children}
    

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

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

    <A render={(data) => <C data={data}></C>}></A>
    

    A组件: {this.props.render(内部state数据)}

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

组件通信

父子组件

props:children props;render props

兄弟组件

消息订阅-发布pubs-sub、集中式管理redux

祖孙组件

消息订阅-发布、集中式管理、context