Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
一 项目结构文件
- const.ts
/** select下拉框 */
export const option = [
{ label: '选项1', key: '1' },
{ label: '选项2', key: '2' },
{ label: '选项3', key: '3' },
]
- service.ts
import request from '@/utils/request';
/** get接口 */
export async function postService(params: any) {
return request('/aa/bb', {
method: 'POST',
data: params
})
}
/** post接口 */
export async function getService (params: any) {
return request('/aa/bb', {
method: 'GET',
params
})
}
- interface.ts文件,一般用于存放ts类型
export interface ItemType {
name: string
age: number | number // | 标示或
fn1: () => void // 无参数,无返回值
fn2: (parame: number) => number // 数字格式参数且返回值为数字格式
fn3: (parame?: number) => void // ?参数可传可不传
fn4: (par1?: number, par2: number) => void // fn4(undefined, 6) 如果不想传第一个参数,可传undefined
}
export interface RouteParams {
taskId: number
}
二 React Hooks(钩子)
- useState
const [list, setList] = useState<ItemType[]>([]) // 创建
setList([...list]) // 修改
- useEffect和useLayoutEffect
** 同步异步:数据变化后重构dom,useLayoutEffect为同步,在dom重构回流渲染完成后执行,useEffect为异步,和dom重构回流同步执行,所以操作dom建议放在useLayoutEffect里,但当有useLayoutEffect时,其他的useEffect因为执行顺序的问题也会变成了同步。
** 执行顺序:当useLayoutEffect和useEffect同时存在时,先调用useLayoutEffect,只有useEffect时,代码从上而下依次执行。
useEffect(() => {
console.log('进入页面执行')
return () => {
console.log('页面销毁时执行')
}
}, [])
useEffect(() => {
console.log('监听list,useState钩子修改了list触发')
}, [list])
useLayoutEffect(() => {
console.log('进入页面,我会先执行,操作渲染后的dom,在这里操作')
}, [])
- useRef
import React, { useRef } from 'react'
const Aaa = () => {
const inputValue = useRef<String>('') // 创建
inputValue.current = '新值' // 修改
const inputDom = useRef<any>() // 创建
inputDom.current.focus() // 获取dom并使input聚焦
return (
<input ref={inputDom} type="text" />
)
}
export default Aaa
- useMemo
** 数据变化后重构dom,useMemo是dom渲染前执行,useLayoutEffect是dom渲染后执行
import React, { useMemo } from 'react'
import { componentA, componentB } from '../components'
const App = () => {
const [ flag, setFlag ] = useState<any>(1)
const Component = useMemo(() => {
console.log('当flag更新后,dom渲染前,触发这里,判断最终渲染哪个组件')
if (flag === 'A') ComponentA
if (flag === 'B') ComponentB
return ComponentC
}, [flag])
return (
<Component type={flag} />
)
}
export default App
- createContext和useContext
** 爷孙组件传值,和vue里的抽取注入类似
import React, { createContext, useContext } from 'react'
// 1、创建,正常需要在爷组件和孙组件挨个引入创建的Context,这里为了方便写博客,爷孙组件都写在一个文件里
const Context = createContext({ name: '', age: 0 });
// 2、传值
const Ye = () => { // 爷组件
const [ name, setName ] = useState('zs'); // 需要传递的值
const [ age, setAge ] = useState(18); // 需要传递的值
return (
<div>
{/** 需要用到变量的子组件一定要写在provider中间并用value传递 */}
<Context.Provider value={{ name, age }}>
<Fu />
</Context.Provider>
</div>
);
};
const Fu = () => { // 父组件
return (
<div style={{ border: '1px solid' }}>
<Sun />
</div>
);
};
// 3、取值
const Sun = () => { // 孙组件
const yeData: any = useContext(Context);
return (
<div style={{ border: '1px solid' }}>
<p>
{yeData.name}:{yeData.age}
</p>
</div>
);
};
export default Ye
- useReducer
** 和useState特性几乎完全相似,可被useEffect监听,并且修改后无法立刻获得最新值,如果多个值相互有关联,可用此钩子,单个值用useState
const [state, dispatch] = useReducer(x => x + 1, 0)
dispatch()
或 -------------- ⬇️
const doFetchDataReducer = (state, action) => {
switch (action.type) {
case 'setData':
return { ...state, data: action.payload }
case 'setIsLoading':
return { ...state, isLoading: action.payload }
case 'setIsError':
return { ...state, isError: action.payload }
default:
throw new Error()
}
}
const [state, dispatch] = useReducer(
doFetchDataReducer,
{ isLoading: false, isError: false, data: 0 }
)
dispatch({ type: 'setData', payload: '2' }) // 修改值
useEffect(() => {
console.log('变了', state); // 可被useEffect监听
}, [state])
三 React Router(路由)
8. useHistory
import { useHistory, useParams } from 'umi'
const { push, replace, location, goBack, goForward, action, listen } = useHistory()
跳转及获参,push添加replace替换,用法完全一样
第一种 // url里显示
1.定义
path: '/aa/bb/:id/:typeId'
2.携参跳转
push(`/aa/bb?id=${record.id}${record.typeId}`)
3.获取
const { id, typeId } = useParams<any>()
第二种 // 显示
1.定义
path: '/aa/bb'
2.携参跳转
push(`/aa/bb?id=${record.id}&typeId=${record.typeId}`)
或
path({pathname: '/home', query: {id: 1, typeId: 2}})
3.获取
const { id, typeId } = location.query
第三种 // 不显示
1. 定义
path: '/aa/bb'
2.携参跳转
path('/aa/bb', {id: 1, typeId: 2})
3.获取
const { id, typeId } = location.state
返回
goBack()
前进
goForward()
获取pathname
const { pathname } = location // /aa/bb
获取跳转类型
const routerType = action // PUSH || REPLACE || ....
监听
listen(callback) // 每次触发都会重复挂载,挂载后任何页面跳转,只要路由变化,都会触发callback函数
const callback = (parameter, routerType) => {
parameter = {
pathname: "/aiLogin",
query: {id: "1"},
search: "?id=1",
...
} // 此页面路由数据
router = 'PUSH' || 'REPLACE' // 跳转类型
}
- history
import { history } from 'umi'
用法和useHistory一样,区别是history是对象,useHistory是函数,需要先在顶部先定义然后调用()
useHistory
const a = useHistory()
a.push('...')
----
history
history.push('...')