React高阶组件在componentWillMount中调用异步action触发两次问题
排查与解决React HOC中componentWillMount触发异步action两次的问题
你遇到的这个问题大概率和两个核心点有关:React Strict Mode在开发环境的重复调用机制,或是使用了已被标记为不安全的生命周期componentWillMount。结合你的代码,我来一步步拆解排查和解决思路:
一、排查步骤
- 检查是否启用了React Strict Mode:在开发环境下,Strict Mode会刻意重复调用某些生命周期方法(包括
componentWillMount、constructor等),用来帮你检测代码中的潜在副作用。如果你的应用根组件包裹在<React.StrictMode>里,这就是开发环境下componentWillMount被触发两次的常见原因,生产环境不会出现这个情况。 - 确认HOC的使用方式:如果你的HOC是在组件的
render方法内部被调用(比如render() { return CheckHasSentGift(MyComponent) }),每次组件渲染都会创建一个新的组件类,导致组件反复卸载挂载,进而触发多次生命周期。不过从你的代码来看,HOC是导出的独立函数,应该是在组件定义时仅调用一次,这个可能性较低,但还是要确认使用场景。 - 排查Redux connect的潜在影响:虽然你的connect用法看起来没问题,但如果HOC内部的组件被意外多次连接,或是state的不必要变化导致组件重复渲染,也可能间接导致action被多次触发,但这不是直接原因。
二、解决方法
1. 替换不安全的componentWillMount为componentDidMount
componentWillMount在React 16.3+已经被标记为不安全生命周期,它可能在服务端渲染、异步渲染场景下被多次调用,而且Strict Mode会刻意重复调用它来暴露问题。换成componentDidMount就可以彻底解决这个问题——它只会在组件挂载完成后调用一次,即使是开发环境的Strict Mode也不会重复触发。
修改后的代码如下:
export default function CheckHasSentGift(TransformedComponent) { @connect(state => state.global, { fetchGift }) class WithRedirect extends Component { // 替换componentWillMount为componentDidMount componentDidMount() { this.props.fetchGift('sent') } render() { // 修复render里的语法问题,用if判断更清晰安全 if (this.props.sentGiftId && !this.props.location.pathname.includes('sent')) { return <Redirect to={`/app/gift/${this.props.sentGiftId}/sent`} /> } return <TransformedComponent {...this.props} /> } } return WithRedirect }
2. (可选)改用函数组件HOC + useEffect
如果你的项目已经迁移到React Hooks,更推荐用函数组件来编写HOC,代码更简洁,也能彻底避开类组件的生命周期陷阱:
import { useEffect } from 'react' import { connect } from 'react-redux' import { Redirect } from 'react-router-dom' export default function CheckHasSentGift(TransformedComponent) { const WithRedirect = ({ sentGiftId, location, fetchGift, ...props }) => { useEffect(() => { // 空依赖数组确保回调仅在组件挂载时执行一次 fetchGift('sent') }, [fetchGift]) if (sentGiftId && !location.pathname.includes('sent')) { return <Redirect to={`/app/gift/${sentGiftId}/sent`} /> } return <TransformedComponent {...props} sentGiftId={sentGiftId} location={location} /> } return connect(state => state.global, { fetchGift })(WithRedirect) }
3. 关于Strict Mode的补充说明
如果确实是Strict Mode导致的开发环境两次调用,其实生产环境不会有这个问题,但还是建议替换生命周期——因为componentWillMount已经被官方废弃,未来React版本可能会彻底移除它。
额外提醒
你原来的render方法里有个语法隐患:this.props.sentGiftId && !this.props.location.pathname.includes('sent') && return <Redirect ... /> 这种写法会因为运算符优先级问题导致潜在bug,换成if判断既清晰又安全,我已经在修改后的代码里做了调整。
内容的提问来源于stack exchange,提问作者Jamie Aden
相关产品推荐
相关产品推荐

