@author: 郭瑞峰 @createTime: 2023/07/10 @updateTime: 2023/07/18
简介一下,前端react hooks是在react@16.8提出的新特性,为了简化 class 类组件,通过 function 函数式组件来实现原来 class 类组件。
先上结论
常用的钩子函数
不废话,直接说一下常用的钩子函数
useState: 用于在函数组件中添加状态useEffect: 用于在组件挂载、更新或卸载时执行副作用操作useCallback: 用于缓存函数实例,避免函数重复创建useMemo: 用于缓存计算结果,避免重复计算useRef: 用于在函数组件中创建 ref 对象
不太常用的钩子函数
useContext: 用于在组件中使用 React ContextuseReducer: 用于在函数组件中使用 reducer 管理状态useLayoutEffect: 类似于 useEffect,但会在浏览器 layout 之后同步执行
详细说明
useState
数据读写
import React, { useState } from 'react'
export default function () {
const [count, setCount] = useState<number>(0)
return (
<div>
<button
onClick={() => setCount(val => val + 1)}
>点了{ count }下</button>
</div>
)
}
useEffect
- 在组件挂载、更新或卸载(生命周期)时执行操作,替代类组件中的生命周期方法
- 订阅外部数据源变化时更新状态或重新渲染组件(简单来说就是监控数据)
- 异步执行
import React, { useState, useEffect } from 'react'
export default function () {
const [count, setCount] = useState<number>(0)
// 挂载、更新、卸载
useEffect(() => {
console.log('组件装载')
return () => {
console.log('组件卸载了')
}
})
return (
<div>
<p>count now is {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
useCallback
- 性能优化
- 防止父组件更新时无关紧要的子组件同步更新
import React, { useState, useCallback } from 'react'
function Child ({ onClick }) {
return (<button onClick={onClick}>点我一下<button>)
}
function Parent () {
const [count, setCount] = useState<number>(0)
const addCount = useCallback(() => {
setCount(val => val + 1)
}, [count])
return (<>
<div>你点击了{ count }次按钮</div>
<Child onClick={addCount} />
</>)
}
useMemo
- 通过将计算结果缓存起来,可以提高组件的渲染性能
import React, { useState, useEffect, useMemo } from 'react'
function App () {
const [second, setSecond] = useState<number>(10)
useEffect(() => {
let timer: any // 这里应该number和空,我偷懒了 ㄟ( ▔, ▔ )ㄏ
if (second > 0) {
clearTimeout(timer)
timer = setTimeout(() => {
setSecond(val => val - 1)
}, 1000)
}
}, [second])
// 写一个有倒计时的按钮
const timerButton = useMemo(() => {
return (<button>{ second }s</button>)
}, second)
return (<>
<div>
{ /* 其他部分 */ }
{ timerButton }
</div>
</>)
}
export default App
useRef
- 保存 DOM 元素的引用
import React, { useRef } from 'react'
function App () {
const inputRef = useRef<HTMLInputElement>(null);
const handleClick = () => {
if (inputRef.current) {
inputRef.current.focus()
}
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Focus Input</button>
</div>
);
};
export default App
useContext(不太常用)
- 函数组件中获取上下文对象中的值,不必通过 props 属性逐层传递
注:该功能一般通过三方库实现全局状态管理(如redux或recoil)
import React, { createContext, useContext } from 'react'
interface Theme {
backgroundColor: string
textColor: string
}
const themes = {
light: {
backgroundColor: '#ffffff',
textColor: '#000000'
},
dark: {
backgroundColor: '#000000',
textColor: '#ffffff'
}
}
const ThemeContext = createContext<Theme>(themes.light)
const App = () => {
return (
<ThemeContext.Provider value={themes.dark}>
<Header />
<Main />
</ThemeContext.Provider>
)
}
const Header = () => {
const theme = useContext(ThemeContext)
return (
<header style={{ backgroundColor: theme.backgroundColor, color: theme.textColor }}>
<h1>Header</h1>
</header>
)
}
const Main = () => {
const theme = useContext(ThemeContext)
return (
<main style={{ backgroundColor: theme.backgroundColor, color: theme.textColor }}>
<h2>Main</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</main>
)
}
export default App
useReducer(不太常用)
- 函数组件中管理状态
- 组件的状态和更新逻辑分离开来管理
注:该功能一般通过三方库实现全局状态管理(如redux或recoil)
import React, { useReducer } from 'react';
type CounterState = {
count: number
}
type CounterAction = {
type: 'increment' | 'decrement',
payload?: number
}
const initialState: CounterState = {
count: 0
}
const reducer = (state: CounterState, action: CounterAction): CounterState => {
switch (action.type) {
case 'increment':
return { count: state.count + (action.payload || 1) }
case 'decrement':
return { count: state.count - (action.payload || 1) }
default:
throw new Error(`Unsupported action type: ${action.type}`)
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState)
const handleIncrement = () => {
dispatch({ type: 'increment', payload: 5 })
};
const handleDecrement = () => {
dispatch({ type: 'decrement' })
};
return (
<div>
<p>Count: {state.count}</p>
<button onClick={handleIncrement}>+5</button>
<button onClick={handleDecrement}>-1</button>
</div>
)
}
export default Counter
useLayoutEffect(有使用,但不频繁)
- 与useEffect相似的
- 同步操作
- 可以用于获取dom尺寸等相关操作
缺点:同步执行,有可能会阻塞 UI 渲染
import React, { useState, useLayoutEffect, useRef } from 'react'
const Counter = () => {
const [count, setCount] = useState<number>(0)
const [rect, setRect] = useState<DOMRect | undefined>(undefined)
const ref = useRef<HTMLDivElement>(null)
useLayoutEffect(() => {
if (ref.current) {
setRect(ref.current.getBoundingClientRect())
}
}, [count])
const handleIncrement = () => {
setCount(count + 1)
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>+1</button>
<div ref={ref}>
{rect && (
<>
<p>Width: {rect.width}</p>
<p>Height: {rect.height}</p>
</>
)}
</div>
</div>
)
}
export default Counter