类组件是一种面向对象思想的体现,类组件之间的状态会随着功能增强而变得越来越臃肿,代码维护成本也比较高,而且不利于后期 tree shaking。所以有必要做出一套函数组件代替类组件的方案,于是 Hooks 也就理所当然的诞生了。
hooks诞生的原因
组件之间的状态复用困难
类组件理解成本较高,业务逻辑分散在生命周期中
class和this的特性较为复杂
解决函数组件无状态只能用于简单组件的痛点,使其拥有持续化的状态
拥抱函数式编程
使用hook注意事项
只在只在最顶层使用Hook
不要在循环, 条件或嵌套函数中调用 Hook
常用hooks
1. useState
解决函数组件无状态只能用于简单组件的痛点,使其拥有持续化的状态。
const [value, setValue] = useState(initValue);
setValue(newValue);
setValue((value) => newValue);
可以在同一个函数组件中被调用多次
在hook中,state状态值不再要求一定是一个对象,可以是数字等简单类型
state的类型是对象时,不会自动合并
无回调函数
多个不同的state,为了更好的复用,建议使用多个useState
2. useEffect
副作用函数,在依赖的数据发生变化时执行,可用来监听状态变化,模拟生命周期等
useEffect(() => {
return () => {
}, [依赖的状态]);
useEffect可以在同一个函数组件中被调用多次
第一个参数是函数,返回值函数在下一次执行前执行
第二个参数是数组,可以指定绑定的关联数据,不传每次更新都会执行,为空数组则只执行一次
useEffect不能被打断
useEffect不能被判断包裹
可用useEffect模拟生命周期
模拟生命周期
useEffect(() => {
return () => {
}, [依赖的状态]);
useEffect 里面使用到的state的值, 固定在了useEffect内部,不会被改变,除非useEffect刷新,重新固定state的值。在依赖中添加count即可触发刷新
const [count, setCount] = useState(0)
useEffect(() => {
console.log('use effect...',count)
const timer = setInterval(() => {
console.log('timer...count:', count)
setCount(count + 1)
}, 1000)
return ()=> clearInterval(timer)
},[])
3. useRef
希望可以实时拿到最新的值,并且不需要添加到依赖不会触发useEffect重新执行,可用来获取dom等。
useEffect 里面使用到的state的值, 固定在了useEffect内部, 不会被改变,除非useEffect刷新,重新固定state的值
import React, { useRef, forwardRef, useImperativeHandle } from 'react'
const ForwardRefIndex = React.forwardRef((props, ref) => <Input {...props} ref={ref} />)
function Home(){
const ref = useRef(null)
useEffect(() => {
console.log(ref.current)
}, [])
return <Input ref={ref} />;
const JMInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus()
return <input type="text" ref={inputRef} />;
function ImperativeHandleDemo() {
const inputRef = useRef();
return (
<button onClick={() => inputRef.current.focus()}>聚焦</button>
<JMInput ref={inputRef} />
</div>
4. useContext
解决通用状态在所有子组件中共享的问题,可以在所有子孙组件中使用状态
const ThemeContext = React.createContext(null)
const Son = () => {
const { color, background } = useContext(ThemeContext);
return <div style={{ color, background, padding: '20px' }}>这是子组件</div>;
const ThemeProvider = ThemeContext.Provider;
export default function ProviderDemo(){
const [ contextValue , setContextValue ] = React.useState({ color:'#ccc', background:'pink' })
return <div>
<ThemeProvider value={ contextValue } >
<Son />
</ThemeProvider>
</div>
5. useMemo
使用缓存避免数据并未更新但是重新render减少渲染
const Child = memo(({data}) =>{
console.log('child render...', data.name)
return (
<div>child</div>
<div>{data.name}</div>
</div>
const Hook = ()=>{
console.log('Hook render...')
const [count, setCount] = useState(0)
const [name, setName] = useState('rose')
const data = { name }
return(
<div>{count}</div>
<button onClick={()=>setCount(count+1)}>update count </button>
<Child data={data}/>
</div>
当点击按钮更新count的时候,Effect组件会render,执行到const data = {name}
这一行代码会生成有新的内存地址的对象,那么就算带着memo的Child组件,也会跟着重新render, 尽管最后其实Child使用到的值没有改变,重复render。
使用useMemo
进行缓存,仅在name更新时再重新触发子组件渲染。
const data = useMemo(()=>{
return { name };
},[name])
6. useCallback
useCallback 解决了函数未发生变化触发子组件更新的问题,
useMemo 是缓存值的
useCallback 是缓存函数的
const Hook = ()=>{
console.log('Hook render...')
const [count, setCount] = useState(0)
const [name, setName] = useState('rose')
const data = { name }
const handleChange = (name) => setName(name);
return(
<div>{count}</div>
<button onClick={()=>setCount(count+1)}>update count </button>
<Child data={data} onChange={handleChange}/>
</div>
7. useReducer
让函数式组件可以像类组件一样集中式管理状态。
可以使用reducer的场景:
如果你的state是一个数组或者对象
如果你的state变化很复杂,经常一个操作需要修改很多state
如果你希望构建自动化测试用例来保证程序的稳定性
如果你需要在深层子组件里面去修改一些状态(关于这点我们下篇文章会详细介绍)
如果你用应用程序比较大,希望UI和业务能够分开维护
const initState = {
name: '',
pwd: '',
isLoading: false,
error: '',
isLoggedIn: false,
function loginReducer(state, action) {
switch(action.type) {
case 'login':
return {
...state,
isLoading: true,
error: '',
case 'success':
return {
...state,
isLoggedIn: true,
isLoading: false,
case 'error':
return {
...state,
error: action.payload.error,
name: '',
pwd: '',
isLoading: false,
default:
return state;
function LoginPage() {
const [state, dispatch] = useReducer(loginReducer, initState);
const { name, pwd, isLoading, error, isLoggedIn } = state;
const login = (event) => {
event.preventDefault();
dispatch({ type: 'login' });
login({ name, pwd })
.then(() => {
dispatch({ type: 'success' });
.catch((error) => {
dispatch({
type: 'error'
payload: { error: error.message }
return (
自定义hook
使用react自带的基本hooks封装在业务中可复用的逻辑。
使用useXXX 的命名规范
遵循Hooks规则
1. useDebounceState:节流修改状态
import { useState, useMemo } from 'react';
export function debounce(fn, time
) {
let timer: any = null;
return (...arg) => {
if (timer) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arg);
}, time);
export function useDebounceState(defaultValue, time = 300) {
const [value, setValue] = useState(defaultValue);
const handleChange = useMemo(() => debounce(setValue, time), [time]);
return [value, handleChange];
export default function Index(){
const [ value , setValue ] = useDebounceState('', 300)
return <div>
{value}
<input value={value} onChange={(e)=>setValue(e.target.value)} />
</div>
2. useUserInfo:userId变化自动获取用户信息
import { useEffect, useState } from 'react';
const sleep = (time = 0) =>
new Promise((resolve: any) => setTimeout(() => resolve(), time));
const fetchUserInfo = async (id: string) => {
await sleep(400);
return {
name: 'alan',
age: 18,
export const useUserInfo = (id: string) => {
const [userInfo, setUserInfo] = useState(null as any);
useEffect(() => {
async function run() {
console.log('run');
const info = await fetchUserInfo(id);
setUserInfo(info);
run();
}, [id]);
return userInfo;
复制代码
- 453
-
我的div丢了肿么办
Vue.js
JavaScript
- 309
-
_island
JavaScript
React.js