React高阶组件渲染无限循环问题:以下代码存在什么错误?
我编写了一个名为ProtectRoute的React高阶组件,代码如下:
export default function ProtectRoute(EnhancedComponent) { @connect(state=>state.something,{callAPI}) class myClass extends Component { componentDidMount() { this.props.callAPI() } render() { return <EnhancedComponent { ...this.props } /> } } return myClass }
我在路由中这样使用它:
<Route exact path='/app' component={ProtectRoute(App)} />
但出现了无限循环,却无法定位原因,请问这段代码存在什么问题?
这个问题的核心原因是你在Route的component属性里直接调用了ProtectRoute(App),这会触发重复创建组件类,进而导致无限的挂载-卸载循环,具体解释如下:
问题根源
当你写<Route exact path='/app' component={ProtectRoute(App)} />时,每次React渲染这个Route组件,都会执行一次ProtectRoute(App)。而你的ProtectRoute函数内部是直接定义了myClass类,每次调用HOC都会返回一个全新的组件类。
React的diff算法会判断前后两次的component属性是否为同一组件类型:如果是不同的类,React会立即卸载之前的组件实例,重新挂载新的组件实例。新组件挂载时会触发componentDidMount,里面调用的callAPI大概率会触发Redux状态更新,状态更新又会导致路由所在的父组件重新渲染,再次执行ProtectRoute(App)创建新组件——如此往复,就形成了无限循环。
修复方案
解决方法非常直接:把HOC的调用移到路由定义之外,提前创建好包装后的组件,这样每次渲染路由时,component引用的都是同一个组件类:
// 提前定义好受保护的App组件(只执行一次) const ProtectedApp = ProtectRoute(App); // 在路由中使用这个预定义的组件 <Route exact path='/app' component={ProtectedApp} />
另外,你也可以优化ProtectRoute本身,把内部的myClass类移到函数外部,避免每次调用HOC都创建新类,但上面的方案已经足够解决当前的无限循环问题。
内容的提问来源于stack exchange,提问作者Thian Kian Phin

