• React Hooks总览


    总览

    hooks 功能分类具体 hooks具体功能React v18新特性跨端支持
    数据更新驱动useState定义要在页面中渲染的数据
    useReducer定义要在页面中渲染的数据,且这个数据有多种处理逻辑
    useSyncExternalStoreconcurrent 模式下,订阅外部 store 的行为,触发更新
    useTransitionconcurrent 模式下,在不阻塞 UI 的情况下来更新状态
    useDeferredValueconcurrent 模式下,延迟更新 UI 的某些部分
    执行副作用useEffect异步状态下,视图更新后,执行副作用
    useLayoutEffect同步状态下,视图更新前,执行副作用
    useInsertionEffect用于处理 CSS-in-JS 缺陷问题
    状态获取与传递useContext接收祖先组件的 context 传递的信息
    useRef存储一个不需要渲染的值
    useImperativeHandle配合 forwardRef 将子组件的 ref 传递给父组件
    状态派生与保存useMemo缓存结果
    useCallback缓存函数
    工具 hooksuseId生成唯一的 ID
    useDebugValue在 React 开发者工具中为自定义 Hook 添加标签

    数据更新驱动

    useState

    useState 允许向组件添加一个 状态变量

    /**
     * @param { any } initValue:初始值
     * @return { array } arr:状态信息
               state { any } 状态名
               setState { function } 修改状态的函数
     */
    const [state, setState] = useState(initValue)
    
    // 第一种方式:传入值
    setState(newValue);
    
    // 第二种方式:传入函数
    setState(preValue => newValue);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    示例:

    const [number, setNumber] = useState(0)
    
    // 第一种方式:传入值
    setNumber(number + 1)
    
    // 第二种方式:传入函数
    setNumber(number => number + 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意事项:

    1. 在函数组件一次执行上下文中,state 的值是固定不变的
    2. 如果两次 setState 传入相同的 state 值,那么组件就不会更新
    3. 当触发 setState 在当前执行上下文中获取不到最新的 state,只有在下一次组件 render 中才能获取到

    useReducer

    useReducer 能够在无状态组件中运行的类似 redux 的功能 api 。

    当对一个状态有多种处理逻辑时建议使用 useReducer。

    /**
     * @param { function } reducer:处理函数
     * @param { any } initValue:初始值
     * @param { function } compareInitValueFn:计算初始值的函数,如果存在则初始值为 compareInitValueFn(initValue)
     * @return { array } arr:状态信息
               state { any } 状态名
               dispatchState { function } 派发状态的函数
     */
    const [state, dispatchState] = useReducer(stateReducer, initValue, compareInitValueFn?)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    示例:

    const numberReducer = (state, action) => {
      switch(action.type) {
        case 'add':
          return state + 1;
        case 'reduce':
          return state - 1;
      }
    }
    const [number, dispatchNumber] = useReducer(numberReducer, 0);
    
    dispatchNumber({
      type: 'add'
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    useSyncExternalStore

    useSyncExternalStore 可以订阅外部 store。

    /**
     * @param { function } subscribe:订阅函数
     * @param { function } getSnapshot:返回组件需要的 store 中的数据快照 带有记忆功能的选择器
     * @param { function } getServerSnapshot:用于 hydration 模式下的 getSnapshot
     * @return { any } snapshot:该 store 的当前快照
     */
    const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例:

    import { combineReducers , createStore  } from 'redux'
    
    /* number Reducer */
    function numberReducer(state = 1, action) {
      switch(action.type) {
        case 'ADD':
          return state + 1
        case 'DEL':
          return state - 1
        default:
          return state
      }
    }
    
    /* 注册reducer */
    const rootReducer = combineReducers({ number: numberReducer })
    /* 创建 store */
    const store = createStore(rootReducer, { number: 1})
    
    function Index(){
      /* 订阅外部数据源 */
      const state = useSyncExternalStore(store.subscribe, () => store.getState().number)
      return (
        
    ) }
    • 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

    注意:

    1. getSnapshot 返回的 store 快照必须是不可变

      如果底层 store 有可变数据,要在数据改变时返回一个新的不可变快照;否则,返回上次缓存的快照。

    2. 如果在重新渲染时传入一个不同的 subscribe 函数,React 会用新传入的 subscribe 函数重新订阅该 store

      可以通过在组件外声明 subscribe 来避免。

    useTransition

    useTransition 可以在不阻塞 UI 的情况下来更新状态。

    /**
     * @return { array } transitionInfo:转换状态信息
              isPending { boolean } 是否存在待处理的转换
              startTransition { function } 将状态由更新状态标记为转换状态
                       @param { function } scope:作用域函数
     */
    const [isPending, startTransition] = useTransition()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例:

    export default function Index(){
      const [number, setNumber] = React.useState(0) // 需要立即响应的任务,立即更新任务
      const [count, setCount] = useState(0) // 不需要立即响应的任务,过渡任务
      const [isPending, startTransition] = useTransition() 
      const btnClick = () => {
         setNumber(number + 1) // 立即更新
         startTransition(() => { // startTransition 里面的任务优先级低
           setCount(count + 1);
         })
      }
      return (
        
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    注意:

    1. 只有在可以访问该状态的 set 函数时,才能将更新包装为转换状态

      如果想响应某个 prop 或自定义 Hook 值启动转换,请尝试使用 useDeferredValue

    2. 传递给 startTransition 的函数必须是同步的

    useDeferredValue

    useDeferredValue 可以延迟更新 UI 的某些部分。

    /**
     * @param { any } value:延迟更新的值
     * @return { any } deferredValue:延迟更新的值
     */
    const deferredValue = useDeferredValue(value)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    export default function Index(){
      const [number, setNumber] = useState(1) // 需要立即响应的任务,立即更新任务
      const deferNumber = useDeferredValue(number) // 把状态延时更新,类似于过渡任务
      const btnClick = () => {
        setNumber(number + 1) // 立即更新
      }
      return (
        
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 在组件的初始渲染期间,返回的延迟值将与提供的值相同
    • 但是在组件更新时,React 将会先尝试使用旧值进行重新渲染(因此将返回旧值)
    • 然后再在后台使用新值进行另一个重新渲染(这时将返回更新后的值)

    注意:应该向 useDeferredValue 传递原始值或在渲染之外创建的对象

    如果在渲染期间创建了一个新对象,并立即将其传递给 useDeferredValue,那么每次渲染时这个对象都会不同,这将导致后台不必要的重新渲染。

    执行副作用

    useEffect

    useEffect 用于异步监听组件的 state 属性,在浏览器绘制执行。

    /**
     * @param { function } setup:回调函数,初始化执行一次,当监听的 state 改变时执行,返回一个当组件被销毁时执行的函数
     * @param { array } dependencies:要监听的 state,默认为所有 state,空数组表示不监听任何 state
     */
    useEffect(setup, dependencies?)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    useEffect(() => {
      let timer = setInterval(() => {
        console.log(1)
      }, 1000)
      return () => {
        timer = null
      }
    }, [])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以把 useEffect 看作是以下三个生命周期的组合:

    • componentDidMount():组件挂载完成执行 callback
    • componentDidUpdate():监听的 state 变化执行 callback
    • componentWillUnmount():组件将要销毁执行 callback 的返回函数

    useLayoutEffect

    useLayoutEffect 是 useEffect 的同步版本,并且是在浏览器绘制执行,主要用于操作 DOM。

    注意:useLayoutEffect callback 中代码执行会阻塞浏览器绘制

    /**
     * @param { function } setup:回调函数,初始化执行一次,当监听的 state 改变时执行,返回一个当组件被销毁时执行的函数
     * @param { array } dependencies:要监听的 state,默认为所有 state,空数组表示不监听任何 state
     */
    useLayoutEffect(setup, dependencies?)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    useInsertionEffect

    useInsertionEffect 可以在布局副作用触发之前将元素插入到 DOM 中。

    注意:useInsertionEffect 是为 CSS-in-JS 库的作者特意打造的。除非正在使用 CSS-in-JS 库并且需要注入样式,否则应该使用 useEffect 或者 useLayoutEffect

    /**
     * @param { function } setup:回调函数,初始化执行一次,当监听的 state 改变时执行,返回一个当组件被销毁时执行的函数
     * @param { array } dependencies:要监听的 state,默认为所有 state,空数组表示不监听任何 state
     */
    useInsertionEffect(setup, dependencies?)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    状态获取与传递

    useContext

    useContext 用于接收祖先组件的 context 传递的信息。

    /**
     * @param { context } context:context 容器
     * @return { any } value:祖先组件传递的 context
     */
    const value = useContext(context)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    // 1、创建 context 容器
    const NumberContext = createContext(null);
    
    // 2、祖先组件定义 Provider
    function GrandFather() {
      return (
        
          
        
      );
    }
    
    // 3、父组件中使用子组件
    function Father() {
      return (
        
      )
    }
    
    // 4、子组件中通过 useContext 接收祖先组件传递的数据
    function Son() {
      const number = useContext(NumberContext);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    useContext() 总是在调用它的组件 上面 寻找最近的 provider。它向上搜索,不考虑 调用 useContext() 的组件中的 provider。

    useRef

    useRef 可以存储一个不需要渲染的值。

    与 state 的区别:ref 的改变不会渲染,state 会

    与普通对象的区别:ref 的值不会重置,普通对象会

    /**
     * @param { any } initValue:初始值
     * @return { ref } ref:只有 current 属性的 ref 对象
     */
    const ref = useRef(initValue)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    function Demo() {
      const divRef = useRef(null)
      return (
        
    ref节点
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    不要在 渲染期间 写入或者读取 ref.current

    可以在 事件处理程序或者 effects 中读取和写入 ref。

    useImperativeHandle

    useImperativeHandle 配合 forwardRef 将子组件的 ref 传递给父组件。

    /**
     * @param { ref } ref:forWardRef 渲染函数中获得的第二个参数
     * @param { function } createHandle:处理函数,返回值作为暴露给父组件的 ref 对象
     * @param { array } dependencies:依赖项
     */
    useImperativeHandle(ref, createHandle, dependencies?)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例:

    // 子组件
    const Son = forwardRef((props, ref) => {
      const divRef = useRef(null)
      useImperativeHandle(ref, () => {
        return {
          sendData: () => {
            console.log(divRef.current)
          }
        }
      }, [])
      return 
    子组件
    })
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    // 父组件
    function Parent() {
      const sonRef = useRef(null)
      return 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    状态派生与保存

    useMemo

    useMemo 在每次重新渲染的时候能够缓存计算的结果

    /**
     * @param { function } calculateValue:要缓存计算值的函数,返回值作为缓存值
     * @param { array } dependencies:依赖项数组
     * @return { any } cachedValue:缓存值(calculateValue 返回的值)
     */
    const cachedValue = useMemo(calculateValue, dependencies)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • cachedValue 初次为不带参数调用 calculateValue 的返回值
    • 后续如果依赖项没有变,就返回上次缓存的值;否则将再次调用 calculateValue,并返回最新结果

    注意:应该仅仅把 useMemo 作为性能优化的手段

    useCallback

    useCallback 允许在多次渲染中缓存函数

    /**
     * @param { function } fn:想要缓存的函数
     * @param { array } dependencies:依赖项数组
     * @return { function } cachedFn:缓存的函数
     */
    const cachedFn = useCallback(fn, dependencies)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 在初次渲染时,useCallback 返回你已经传入的 fn 函数
    • 在之后的渲染中, 如果依赖没有改变,useCallback 返回上一次渲染中缓存的 fn 函数;否则返回这一次渲染传入的 fn。

    注意:

    1. 不应在循环或者条件语句中调用 useCallback
    2. 应该仅仅把 useMemo 作为性能优化的手段

    工具 hooks

    useId

    useId 可以生成传递给无障碍属性的唯一 ID。

    /**
     * @return { string } id:唯一的字符串 ID
     */
    const id = useId()
    
    • 1
    • 2
    • 3
    • 4

    注意:不要使用 useId 来生成列表中的 key

    useDebugValue

    useDebugValue 可以在 React 开发工具 中为自定义 Hook 添加标签。

    /**
     * @param { any } value:在 React 开发工具中显示的值
     * @param { function } format:如果传入,则值为将 value 作为参数调用 format 返回的值;否则值为 value
     */
    useDebugValue(value, format?)
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    Redis应用场景
    个人百度百科怎么创建
    Vue--router和route的区别
    浅谈Oracle数据库调优(3完)
    15-Groovy-日期和时间
    Python机器学习分类算法(一)-- 朴素贝叶斯分类(Naive Bayes Classifier)
    [NCTF2019]Fake XML cookbook XML注入
    图解选择排序算法及优化
    JSTL使用
    Zookeeper分布式应用协调软件的核心概念以及部署
  • 原文地址:https://blog.csdn.net/Jackson_Mseven/article/details/132639273