Hook 本质上就是一个函数,它简洁了组件,有自己的状态管理,生命周期管理,状态共享。
Hook 解决了:
- 组件之间状态复用
- 在函数组件中可以使用生命周期
- 不区分组件时间使用场景
React 内置的 Hook
- 💚useState — 状态管理
- 💚useEffect — 生命周期管理
- 💚useMemo — 缓存值
- 💚useRef — 获取 Dom 操作
- 💚useCallback — 缓存函数
- useReducer — redux 相似
- useImperativeHandle — 子组件暴露值/方法
- useLayoutEffect — 副作用会在 DOM 更新之后同步执行,会阻塞浏览器绘制
- 与useEffect区别:useEffect是会在整个页面渲染完才会调用
react-redux 的 Hook
- useSelector() — 从redux的store队形中提取数据(state)
- useDispatch() — 返回Redux store中对dispatch函数的引用
- useStore() — 返回redux
<Provider>
组件的store对象的引用
useState
- 作用:为组件添加state,并通过setState来更新state,避免重复创建initialState
- 用法:const [state, setState] = useState(initialState)
- setState(设置变量)
- useState(初始化变量)
- 注意:setState()在React中大部分是异步情况,但也存在同步情况
- 在setTimeout 回调函数中调用会同步修改
- 在原生DOM事件的回调函数中调用会同步修改
useEffect
- 作用:处理副作用(包括网络请求、直接操作DOM,使用计时器函数等),相当于compoentDidmount和compoentDidUpdate
- 用法:useEffect(callback, [dependenices])
- 示例:
1
2
3
4
5
6useEffect(() => {
// 依赖项变化时执行的语句
return () => {
//(可选)清除useEffect,相当于compoentDidUnmount
}
}, [依赖项 || '为空则只执行一次'])
useRef
- 注意点:
- 改变 ref.current 属性时,React 不会重新渲染组件,而useState会触发页面的重新渲染
- 更新useRef是副作用,所以一般写在useEffect或event handler里
- 使用useRef来保存不影响组件视图输出信息 — 清除定时器
- 作用:通过ref操作DOM,避免重复创建 ref 的内容.
- 用法:const ref = useRef(initialValue)
- 使用:ref.current = 子组件的实例
- 解决什么问题?答:用来获取上一次的值
1
2
3
4
5
6
7const usePrevious = (value: string | number) => {
const ref = useRef()
useEffect(() => {
ref.current = value
})
return ref.current
}
useImperativeHandle
- 作用:向父组件暴露一个自定义的 ref(穿透ref),应当与forwardRef一起使用(包在forwardRef里面)
- 用法:useImperativeHandle(ref,createHandle,dependencies)
- ref:子组件向父组件暴露的实例
- createHandle:函数,传递的父组件可操作的实例和方法
- dependencies:监听状态,更新状态
useMemo
- 作用:跳过不必要的计算和渲染实现性能的优化,缓存每次重新渲染都需要计算的结果
- 用法:const newValue = useMemo(callback,[dependencies])
- 只有依赖改变,才会重新执行
- 返回值:初次渲染,返回不带参数调用的计算的结果,依赖不变,返回上次缓存的值,改变则返回新值
- 只有依赖改变,才会重新执行
useCallback
- 概念:useMemo的语法糖,缓存的是一个函数,返回的也是一个函数
- 用法:useCallback(fn,[dependencies])
- 与useMemo的区别:useCallback不会执行第一个参数函数,而是将『函数返回』,而useMemo会执行第一个函数并且将『结果返回』
useReducer
- 作用:向组件添加一个管理状态的reducer,实现reducer函数,避免重新创建初始值
- 用法:const [state, dispatch] = useReducer(reducer, initialArg, init?)
- reducer:用于更新state的纯函数。参数为
state
和action
,返回值是更新后的state - initialArg:用于初始化state的任意值
- init:用于计算初始值的函数。如果存在,使用
init(initialArg)
的执行结果作为初始值,否则使用initialArg
- dispatch:用于更新 state 并触发组件的重新渲染
- 用法:dispatch(newVAr)
- reducer:用于更新state的纯函数。参数为
下面的Hook主要是用于请求api,并处理数据
参考 TanStack Query 中文文档
useInfiniteQuery
- 作用:无限滚动翻页
- 用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14const {
data, // 包含无限查询数据的对象,data.pages数组包含已获取的分页页面,data.pageParams数组包含用于获取分页页面的参数
hasNextPage, // 含义:存在下一页 值:布尔值,如果getNextPageParam返回的不是undefined的,则为true
hasPreviousPage, // 含义:存在上一页 值:布尔值,如果getPreviousPageParam返回的不是undefined的,则为true
isFetchingNextPage, // 值:布尔值,区分后台刷新状态和加载更多状态
isFetchingPreviousPage, // 值:布尔值,区分后台刷新状态和加载更多状态
refetch
} = useInfiniteQuery({
queryKey: ["projects"], // 这里的key相当于useEffect的依赖,但是为了避免多个key都是同样的,最好在加个字符串,使queryKey唯一
queryFn: fetchProjects, // async/await请求api的函数
// 以下两个方法都是用于确定是否有更多数据要加载和或用来获取它所需的信息
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
});
useQuery/useQueries
- 作用:获取请求接口的数据(并行请求)
- 用法:
1
2
3
4
5
6
7const { data } = useQuery({
queryKey: ['issues'],
queryFn: 接口的函数,
suspense: true, // 不需要处理加载或错误的状态
enabled: false // 存在才会执行
})
useQueries([{queryKey:[],queryFn:Fn},{queryKey:[],queryFn:Fn}])
react-redux 的 hooks
- 引入 import { hook } from ‘react-redux’
useSelector() — 从redux的store队形中提取数据(state)
- 示例: const counter = useSelector( state => state.counter)
useDispatch() — 返回Redux store中对dispatch函数的引用
- 简单理解就是把dispatch方法暴露出来使用
- 示例:
1
2const dispatch = useDispatch()
<button onClick={() => dispatch({ type: 'increment-counter'})}>julie</button> - 将dispatch传递给子组件,使用useCallback
- 示例:
1
2
3
4const incrementCounter = useCallback(()=>
dispatch({type: 'increment-counter'}
), [dispatch])
<MyIncrementButton onIncrement={incrementCounter} /> - 然后暴露该组件给其它组件使用
useStore() — 返回redux<Provider>
组件的store对象的引用
- 示例:
1
2const store = useStore
<div>{store.getStore}</div> // 但store中的state更新,这里不会自动跟新,所以适合存放不变的数据