好久没写技术文章了,最近翻看了自己刚学 React 时做的笔记,决定整理一下我常用的八个 React Hooks(如果算上自定义 Hooks 应该是九个,但今天先聚焦这八个)。我刚开发时是个 React 小白,看了一天公司项目后发现必须补习 Hooks,于是周末恶补了一波,做了些总结。这篇文章将结合代码示例和实际场景,带你逐一掌握这些 Hooks。虽然内容有点长,但你可以挑自己感兴趣的 Hooks 重点阅读。准备好了吗?让我们一起解锁 React Hooks 的魅力吧!
在 React 中,状态管理是核心。类组件用 this.state 定义状态,而函数组件则靠 useState。来看看两者的对比:
import React from 'react';
class StateClass extends React.Component {
constructor() {
super();
this.state = { name: '类' };
}
setName = () => {
this.setState({ name: '我通过类组件方法变成这样了' });
};
render() {
return (
这是一个类组件 —— {this.state.name}
);
}
}
export default StateClass;
函数组件实现
import React, { useState } from 'react';
function StateFunction() {
const [name, setName] = useState('函数');
return (
setName('我使用 Hooks 变成这样了')}>
这是一个函数组件 —— {name}
);
}
export default StateFunction;
使用要点:
场景:需要管理简单状态时,比如表单输入、计数器等。相比类组件,useState 写法更简洁,直观易懂。
副作用是指渲染之外的操作,比如数据请求、事件绑定、手动改 DOM 等。useEffect 就是为函数组件添加“生命周期”功能的利器,执行时机在渲染结束后。
import React, { useState, useEffect } from 'react';
function Counter() {
const [num, setNum] = useState(0);
useEffect(() => {
console.log('渲染结束啦!');
});
return (
setNum(num + 1)}>
点击我 —— {num}
);
}
空依赖:只执行一次
useEffect(() => {
console.log('只在首次渲染时执行');
}, []);
指定依赖:依赖变化时执行
useEffect(() => {
console.log('num 变了,重新执行');
}, [num]);
绑定事件时,必须清理,否则会重复绑定导致性能问题:
useEffect(() => {
const updateMouse = (e) => {
console.log(`鼠标位置:${e.clientX}, ${e.clientY}`);
};
document.addEventListener('click', updateMouse);
return () => {
document.removeEventListener('click', updateMouse);
console.log('清理完成');
};
}, []);
执行顺序:
场景:数据请求、事件监听、定时器等。空依赖适合初始化加载,带依赖适合动态更新。
useLayoutEffect 和 useEffect 用法相同,但执行时机不同:
import React, { useState, useEffect, useLayoutEffect } from 'react';
function Demo() {
const [num, setNum] = useState(0);
useLayoutEffect(() => {
console.log('useLayoutEffect 先执行');
}, [num]);
useEffect(() => {
console.log('useEffect 后执行');
}, [num]);
return (
setNum(num + 1)}>
点击我 —— {num}
);
}
场景:需要同步操作 DOM(比如测量元素尺寸、调整布局)时用 useLayoutEffect,其他情况优先 useEffect 以避免阻塞渲染。
useMemo 缓存计算结果,只有依赖变化时才重新计算,避免不必要的重复执行。
import React, { useState, useMemo } from 'react';
function Calculator() {
const [num, setNum] = useState(1);
const [age, setAge] = useState(18);
const doubleNum = useMemo(() => {
console.log(`计算双倍:${num}`);
return num * 2; // 假设复杂计算
}, [num]);
return (
setAge(age + 1)}>
双倍值:{doubleNum}
年龄:{age}
);
}
未优化:每次渲染都重新计算 doubleNum,即使 num 不变。
优化后:只有 num 变化时才重新计算。
import React, { useState, useMemo } from 'react';
import { memo } from 'react';
const Child = memo(({ info }) => {
console.log('子组件渲染');
return 姓名:{info.name}
;
});
function Parent() {
const [show, setShow] = useState(true);
const info = useMemo(() => ({ name: 'Even', age: 22 }), []);
return (
);
}
场景:复杂计算、防止子组件因父组件无关更新而重复渲染。
import React, { useState, useMemo, useCallback } from 'react';
function Demo() {
const [num, setNum] = useState(1);
const [age, setAge] = useState(18);
const memoizedValue = useMemo(() => num * 2, [num]); // 返回值
const memoizedFn = useCallback(() => num * 2, [num]); // 返回函数
return (
setAge(age + 1)}>
Memo 值:{memoizedValue}
Callback 值:{memoizedFn()}
年龄:{age}
);
}
优化子组件
import React, { useState, useCallback } from 'react';
function Child({ callback }) {
useEffect(() => {
console.log('子组件更新');
}, [callback]);
return 结果:{callback}
;
}
function Parent() {
const [num, setNum] = useState(1);
const getDoubleNum = useCallback(() => num * 2, [num]);
return (
setNum(num + 1)}>
父组件:{num}
);
}
场景:传递函数给子组件时,避免因函数引用变化导致子组件重复渲染。
useRef 返回一个在组件生命周期内保持不变的引用对象,常用于保存数据或 DOM 引用。
import React, { useState, useEffect, useRef } from 'react';
function Timer() {
const [num, setNum] = useState(0);
const timerRef = useRef();
useEffect(() => {
timerRef.current = setInterval(() => setNum(n => n + 1), 400);
}, []);
useEffect(() => {
if (num > 10) {
clearInterval(timerRef.current);
console.log('定时器已清除');
}
}, [num]);
return 计数:{num};
}
未使用 useRef:定时器 ID 丢失,无法清除。 使用 useRef:通过 ref.current 保存 ID,精准清除。
特点:修改 ref.current 不会触发重新渲染。
场景:保存定时器、访问 DOM 元素(如聚焦输入框)。
useContext 让子组件轻松共享父组件的状态,避免逐层传递 props。
function Parent() {
const [num, setNum] = useState(1);
return (
);
}
使用 useContext
import React, { useState, useContext, createContext } from 'react';
const NumContext = createContext(null);
function Parent() {
const [num, setNum] = useState(1);
return (
);
}
function Child1() {
const num = useContext(NumContext);
return 子组件 1:{num}
;
}
function Child2() {
const num = useContext(NumContext);
return 子组件 2:{num + 2}
;
}
useReducer 类似 Redux,适合管理复杂状态逻辑。
import React, { useReducer } from 'react';
const initialState = { age: 18, num: 1 };
const reducer = (state, action) => {
switch (action.type) {
case 'add':
return { ...state, num: state.num + 1 };
default:
return state;
}
};
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
计数:{state.num}
年龄:{state.age}
);
}
这八个 Hooks 是 React 函数组件的基石:
从类组件的繁琐到 Hooks 的简洁,React 的开发体验变得更优雅。希望这篇文章能帮你快速上手这些 Hooks,并在项目中灵活运用。如果你有疑问或想聊聊自定义 Hooks,欢迎留言交流!
关键词:React Hooks 教程、useState 示例、useEffect 副作用、useMemo 优化、React 函数组件开发。