几种常用的自定义hooks

自 React 16.8 之后,推荐使用函数式组件

Hooks 相关知识点参考:React Hooks完全上手指南

以下记录一些项目中经常用到的自定义hooks

useAsync:异步接口请求

import { useState, useCallback } from 'react'

export default const useAsync = (asyncFunc) => {
  // 设置三个异步逻辑相关的 state
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)
  // 定义一个 callback 用于执行异步逻辑
  const execute = useCallback(() => {
    // 请求开始时,设置 loading 为 true,清除已有数据和 error 状态
    setLoading(true)
    setData(null)
    setError(null)
    return asyncFunc().then(res => {
      // 请求成功,将数据写进 state,设置 loading 为 false
      setData(res)
      setLoading(false)
    }).catch(err => {
      // 请求失败,设置 loading 为 false,并设置错误状态
      setError(error)
      setLoading(false)
    })
  }, [asyncFunc])

  return { execute, loading, data, error }
}

在组件中使用示例

import React from 'react'
import useAsync from './useAsync'

export default function UserList(props) {
  // 通过 useAsync 这个函数,只需要提供异步逻辑的实现
  const {
    execute: fetchUsers,
    data: users,
    loading,
    error,
  } = useAsync(async () => {
    const res = await fetch('http://xxx/xxx')
    const json = await res.json
    return json.data
  })

  return (
    // 根据状态渲染 UI...
  )
}

useUpdate:更新时执行

import { useRef, useEffect } from 'react'

export default function useUpdate(
  callback = () => {},
  dependences,
  initialData = false
) {
  const isInitialMount = useRef(true)
  useEffect(() => {
    // 第一次,也就是mount阶段 不执行onChange,只有后续更新的时候才调用
    // 因为在页面中,一般mount阶段会写请求数据之类的操作
    if (isInitialMount.current && !initialData) {
      isInitialMount.current = false
    } else {
      callback()
    }
  }, dependences)
}

使用示例

useUpdate(() => {
  console.log(count)
}, [count])

useForceUpdate:强制更新

import { useReducer, useLayoutEffect, useRef } from 'react'

export default function useForceUpdate() {
  // 函数组件没有forceUpdate,用这种方法代替
  const [ignored, forceUpdate] = useReducer(x => x + 1, 0)
  const resolveRef = useRef(null)

  useLayoutEffect(() => {
    resolveRef.current && resolveRef.current()
  }, [ignored])

  const promise = () => {
    return new Promise((resolve, reject) => {
      forceUpdate()
      resolveRef.current = resolve
    })
  }

  return { forceUpdate: promise }
}

使用示例

// 函数组件没有forceUpdate,用这种方法代替
const { forceUpdate } = useForceUpdate()
useEffect(() => {
  forceUpdate().then(() => {
    // 得等到上一次渲染完成后,才能拿到最新的宽度和高度
    chart?.forceFit()
  })
}, [count])

useEvent:封装事件处理函数

主要有两个特点

  • 在组件多次render时保持引用一致。
  • 函数内始终能获取到最新的propsstate
import { useLayoutEffect, useCallback, useRef } from 'react'

export default function useEvent(handler) {
  const handlerRef = useRef(null)
  // 视图渲染完成后更新 handlerRef.current 指向
  // 事件回调触发的时机是在视图完成渲染之后,这样可以稳定获取到最新的state与props
  useLayoutEffect(() => {
    handlerRef.current = handler
  })
  // 用useCallback包裹,使得render时返回的函数引用一致
  return useCallback((...args) => {
    const fn = handlerRef.current
    return fn(...args)
  }, [])
}

使用示例

import { useState } from 'react'

function Demo() {
  const [text, setText] = useState('')

  // 不管render多少次,onInputChange都是不变的,做节流、防抖等
  const onInputChange = useEvent((value) => {
    setText(value)
  });

  return 
}

usePrevious:记录上一次的值

import { useEffect, useRef } from 'react'

export default function usePrevious(value) {
  const ref = useRef()

  useEffect(() => {
    ref.current = value
  }, [value])

  return ref.current
}

使用示例

const [count, setCount] = useState(1)
const preCount = usePrevious(count)

useScroll:监听滚动位置

import { useState, useEffect } from 'react'
// 获取横向,纵向滚动条位置
export default const getPosition = () => {
  return {
    x: document.body.scrollLeft,
    y: document.body.scrollTop,
  }
}
export default const useScroll = () => {
  // 定一个 position 这个 state 保存滚动条位置
  const [position, setPosition] = useState(getPosition())
  useEffect(() => {
    const handler = () => {
      setPosition(getPosition())
    }
    // 监听 scroll 事件,更新滚动条位置
    document.addEventListener('scroll', handler)
    return () => {
      // 组件销毁时,取消事件监听
      document.removeEventListener('scroll', handler)
    }
  }, [])
  return position
}

返回顶部功能示例

import React, { useCallback } from 'react'
import useScroll from './useScroll'

export default function ScrollTop (props) {
  const { y } = useScroll()
  const goTop = useCallback(() => {
    document.body.scrollTop = 0
  }, [])

  const style = {
    position: 'fixed',
    right: '10px',
    bottom: '10px',
  }
  // 当滚动条位置纵向超过300时,显示返回顶部按钮。否则不渲染任何UI
  if (y <= 300) return null
  return (
    
  )
}

你可能感兴趣的:(几种常用的自定义hooks)