在React中,"回调"通常指把函数作为参数传递给另一个组件或Hook,在特定时机被调用。
贯穿事件处理、状态更新、副作用、性能优化、组件通信等场景。
- 事件处理中的回调
最基础的形式,把函数传给JSX的事件属性
javascript
function Button () {
const handleClick = () => {
console.log('clicked')
}
return <button onClick={handleClick}>点我</button>
}
这个{handleClick}是传递函数引用,
需要传参时用箭头函数包装:
<button onClick={() => handleDelete(id)}>删除</ button>
- 回调作为props:父子通信
父组件把函数传给子组件,子组件调用它把数据回传给父组件
javascript
function Child({ onSelect }) {
return <button onClick={() => onSelect('hello')}>选择</button>
}
function Parent() {
const handleSelect = (value) => console.log(value)
return <Child onSelect={handleSelect} />
}
- 回调中的闭包陷阱
这是React回调里最容易踩的坑,函数组件每次渲染都会创建新的作用域,回调会捕获当次渲染的变量
javascript
function Counter() {
const [count, setCount] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
console.log(count) // 永远是 0
}, 1000)
return () => clearInterval(timer)
}, []) // 空依赖,回调闭包捕获了初始 count
}
解决办法:
- 把依赖写进依赖数组 count
- 用 setCount(c => c + 1) 的函数式更新
- 用 useRef 保存最新值
- 用 useEffectEvent(React 实验性 API)读取最新值而不重建回调
- useCallBack:缓存回调引用
每次渲染都会重新创建函数,如果作为prop传给用React.memo包裹的子组件,会导致子组件无意义的重渲染
javascript
const handleClick = useCallback(() => {
doSomething(a, b)
}, [a, b])