概念
Class 组件问题:大型组件很难拆分和重构,难于测试,逻辑混乱,复用逻辑复杂如HOC、Render Prop;
函数组件问题:没有组件状态,没有生命周期;
Hooks:有生命周期的函数
规则:调用顺序必须保持一致,即所有钩子函数不能嵌套于 if、for
useState
- 第一次 render 会初始化 state,re-redner(props 发生变化) 不会重新设置而是替换旧值,只能通过 set 进行修改;
const [count, setCount] = useState<number>(() => 0) // 函数式
- set 属于异步更新即在函数中多次使用 set 会合并操作, 在
react18
之前合成事件为异步更新,原生事件是同步更新,在react18
皆为异步更新
const handleClick = () => {
setCount(count + 1)
setCount(count + 1)
} // + 1
- 函数无法合并,即每次返回上一次周期的值,可以使用函数形式
const handleFucClick = () => {
setCount(count => count + 1)
setCount(count => count + 1)
}
- 强制更新
const handleSyncClick = () => {
flushSync(() => {
setCount(count => count + 1)
})
flushSync(() => {
setCount(count => count + 1)
})
}
useEffect
- 没有依赖时(
useEffect(() => {})
)可理解为DidMount
+DidUpdate
,组件初始化和 props 更新都会执行 - 依赖为空值(
useEffect(() => {},[]
) )相当于DidMount
- 存在依赖时,相当于
DidMount
+ 依赖DidUpdate
- 依赖为引用类型会形成死循环,原因是 React 内部使用
Object.is
进行对比,引用地址不一致导致一直进行变化 - return () => {} 相当于模拟
willUnMount
但不等于,当props
发生变化如果没有依赖或者是监听的props
中的属性,也会执行此函数
useEffect(() => {
console.log('开始')
return () => {
console.log('结束 ')
}
}, [])
useContext
获取上下文,相比于 Context.Consumer 简单了许多
const StateContext = React.createContext(state)
const UseContextChild: React.FC = () => {
const value = useContext(StateContext)
return <div></div>
}
memo
当父组件更新时子组件也会无条件进行重新渲染,使用memo
进行包裹缓存组件,相当于 PureCompoennt
const Child: React.FC = memo(() => {}
useMemo
当组件中的某一个值发生改变时,如果存在需要计算的值会重新进行计算,而 useMemo
可以缓存计算结果,只有在依赖项发生变化时才重新计算,类似于 vue 的计算属性
const info = useMemo(() => {
return {
// ...
}
}, [])
useCallback
父组件传入子组件i一个函数,当父组件发生变化,子组件也会重新渲染,将函数缓存配合 memo 使用
const onchange = useCallback(() => {
// ...
}, [])
useReducer
相当于 useState 加大版,用于处理更庞大且复杂的数据
const initState = { count: 0 }
const reducer = (state = initState, action: any) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count + -1 }
default:
return state
}
}
const [state, dispatch] = useReducer(reducer, initState)
useRef
获取真实 dom 元素
const btnRef = useRef(null)
<div ref={btnRef}>useRef</div>
useTranstion
它用于在处理异步操作时提供平滑的过渡效果,可以帮助你在组件的状态变化时,以动画的方式过渡到新的状态。
自定义 hooks
文件名和函数必须以use
开头函数式声明,相当于一个有钩子的函数,返回需要的状态
const useMousePosition = () => {
const [x, setX] = useState(0)
const [y, setY] = useState(0)
useEffect(() => {
function mouseMoveHandler(event: MouseEvent) {
setX(event.clientX)
setY(event.clientY)
}
document.body.addEventListener('mousemove', mouseMoveHandler)
return () => document.body.removeEventListener('mousemove', mouseMoveHandler)
}, [])
return [x, y]
}
export default useMousePosition
闭包陷阱
闭包陷阱是指在使用 useState 或 useEffect 这类 Hook 时,由于函数组件的特性,闭包的状态值可能不会按预期更新
const hook = () => {
let num = 0
const effect = () => {
num += 1
const message = `现在的 num 是 ${num}`
console.log('message', message)
return function umount() {
console.log(message)
}
}
return effect
}
const add = hook()
const umount = add() // 1
add() // 2
add() // 3
add() // 4
add() // 5
umount() // 1 因为作用域问题,取到第一个