Hook 本质上就是一个函数,它简洁了组件,有自己的状态管理,生命周期管理,状态共享。
Hook 解决了:
- 组件之间状态复用
- 在函数组件中可以使用生命周期
- 不区分组件时间使用场景
React 内置的 Hook
- 💚useState — 状态管理
- 💚useEffect — 生命周期管理
- useContext — 共享状态数据
- 💚useMemo — 缓存值
- 💚useRef — 获取 Dom 操作
- 💚useCallback — 缓存函数
- useReducer — redux 相似
- useImperativeHandle — 子组件暴露值/方法
- useLayoutEffect — 完成副作用操作,会阻塞浏览器绘制
useState
- 作用:为组件添加state,并通过setState来更新state,避免重复创建initialState
- 用法:const [state, setState] = useState(initialState)
- setState(设置变量)
- useState(初始化变量)
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
}
useContext
- 作用:跨级组件通信(全局传递数据),读取和订阅组件中的 context
- 参考:想了解useContext更多内容,点击查看
- 用法:
创建一个单独的文件来管理
1
2
3// store/context.tsx
import { createContext } from 'react'
export const ThemeContext = createContext(false)在顶级组件中使用Context.Provider来进行包裹,值通过value={}传递,包裹的内部组件都可以使用
- 示例:
- 需求:这个ThemeContext会随着点击事件而改变,并且应用于全局
- 可以在App.tsx文件中使用,也可以在全局的布局组件中使用,这里在全局的布局组件中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import React from 'react'
import { memo, useState } from 'react'
import { ThemeContext } from '@store/context'
const Layout = memo((props) => {
const [theme, setTheme] = useState(false)
const { children } = props
const sunMoon = classNames(`${theme
? 'bg-primary text-black transition-all'
: 'bg-black text-primary transition-all'}`)
return (
<ThemeContext.Provider value={theme}>
<div className={`imgBg ${sunMoon}`}>
<SideColumn
changeWAB={()=> setTheme(!theme)}
/>
<div>{children}</div>
</div>
</ThemeContext.Provider>
)
})
export default Layout;
- 子组件通过useContext()来拿到传递过来值,直接使用
1
2
3
4
5
6import React from 'react'
import { memo, useState, useContext } from 'react'
import { ThemeContext } from '@store/context'
const SideColumn = memo<sideColumnProps>((props) => {
const theme = useContext(ThemeContext)
})
useMemo
- 作用:跳过不必要的计算和渲染实现性能的优化,缓存每次重新渲染都需要计算的结果
- 用法:const newValue = useMemo(callback,[dependencies])
- 只有依赖改变,才会重新执行
- 返回值:初次渲染,返回不带参数调用的计算的结果,依赖不变,返回上次缓存的值,改变则返回新值
- 只有依赖改变,才会重新执行
useCallback
- 概念:useMemo的语法糖,缓存的是一个函数,返回的也是一个函数
- 用法:useCallback(fn,[dependencies])
useImperativeHandle
- 作用:向父组件暴露一个自定义的 ref 句柄,应当与forwardRef一起使用(包在forwardRef里面)
- 用法:useImperativeHandle(ref,createHandle,dependencies)
- ref:子组件向父组件暴露的实例
- createHandle:函数,传递的父组件可操作的实例和方法
- dependencies:监听状态,更新状态
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}])