this 绑定与闭包
约 551 字大约 2 分钟
布欧-Lewyon
2026-05-15
告别 this
// 类组件 —— this 无处不在
class UserCard extends React.Component {
handleClick() {
// ❌ this 是 undefined(需要 bind 或箭头函数)
console.log(this.props.userId)
}
// 方式 A:箭头函数(推荐)
handleDelete = () => {
fetch(`/api/users/${this.props.userId}`, { method: 'DELETE' })
}
// 方式 B:构造函数 bind
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
// 方式 C:render 内箭头函数(不推荐,每次渲染新函数)
render() {
return <button onClick={() => this.handleClick()}>Delete</button>
}
}// Hooks 版本 —— 没有 this
function UserCard({ userId }: { userId: number }) {
const handleDelete = useCallback(() => {
fetch(`/api/users/${userId}`, { method: 'DELETE' })
}, [userId])
return <button onClick={handleDelete}>Delete</button>
}闭包陷阱
函数组件的最常见陷阱——闭包捕获的是创建时的值。
// 典型问题:定时器闭包
function Timer() {
const [count, setCount] = useState(0)
// ❌ 闭包捕获了 count = 0
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1) // 始终 setCount(1)
}, 1000)
return () => clearInterval(id)
}, [])
}解决方案对比
// 方案 1:函数式更新(推荐)
setCount(prev => prev + 1) // prev 永远最新
// 方案 2:正确设置依赖
useEffect(() => {
const id = setInterval(() => setCount(count + 1), 1000)
return () => clearInterval(id)
}, [count]) // count 变化重建定时器
// 方案 3:useRef 保存最新值
const countRef = useRef(count)
countRef.current = count
useEffect(() => {
const id = setInterval(() => {
setCount(countRef.current + 1) // 从 ref 读最新值
}, 1000)
return () => clearInterval(id)
}, [])useCallback 闭包
function Parent() {
const [count, setCount] = useState(0)
// ❌ 闭包:count 被冻结在创建时的值
const logCount = useCallback(() => {
console.log(count)
}, []) // 空依赖:永不更新函数引用
// ✅ 正确
const logCount = useCallback(() => {
console.log(count)
}, [count])
// ✅ 最佳实践:useRef 逃避依赖
const countRef = useRef(count)
countRef.current = count
const logCount = useCallback(() => {
console.log(countRef.current) // 永远最新
}, [])
}迁移模式对照
| 类组件 | Hooks |
|---|---|
this.handleClick = this.handleClick.bind(this) | 直接定义函数 |
onClick={this.handleClick} | onClick={handleClick} |
const that = this / self | 不需要 |
this.setState({ count: this.state.count + 1 }) | setCount(prev => prev + 1) |
this.props.xxx | props.xxx 或解构 |
this.state.xxx | xxx 直接使用 |
小结
- 类组件中
this需要 bind,Hooks 不需要,函数定义即用。 - 闭包陷阱是 Hooks 迁移中最常见的 Bug:useEffect/useCallback 内使用 state 需注意依赖。
- 函数式更新
setCount(prev => prev + 1)是最简单的解决方案。 useRef保存最新值可穿越闭包边界。- ESLint 插件
react-hooks/exhaustive-deps自动检测遗漏依赖。
