react-router4.x,嵌套路由,切换子路由的时候,父组件被卸载重新render?

父组件是/course/courseDetails,里面加了嵌套路由,在切换子路由时会触发父组件componentWillUnmount卸载,然后父组件componentDidMount重新渲染,在渲染子组件,这是为啥啊。由React Router引起的组件重复渲染谈Route的使用姿势,看到一个关于这个类似的情况,但是我没有给父组件传递props更新啊。

 "react": "^16.4.0",
 "react-router": "^4.3.1",
 "react-router-dom": "^4.3.1",

<Col span={18} >
  <div className='c-d-contents-l'>
   <Menu onClick={this.handleClick} selectedKeys={[current]} mode="horizontal">
       <Menu.Item key="/course/courseDetails/courseCatalog">目录</Menu.Item>
       <Menu.Item key="/course/courseDetails/courseSetting">设置</Menu.Item>
       <Menu.Item key="/course/courseDetails/studentList">名单</Menu.Item>
   </Menu>
   <Switch>
       <Route path="/course/courseDetails/courseCatalog" component={CourseCatalog} exact={false}/>
       <Route path="/course/courseDetails/courseSetting" component={CourseSetting} exact={false}/>
       <Route path="/course/courseDetails/studentList" component={StudentList} exact={false}/>
   </Switch>
  </div>
</Col>
阅读 1k
avatarAI BotBETA

在 React Router v4.x 中,如果你遇到嵌套路由时父组件被卸载并重新渲染的问题,这通常不是由 React Router 本身直接引起的,而是可能与你的组件结构或状态管理方式有关。不过,从你提供的代码片段来看,有几个可能的原因和解决方案:

可能的原因

  1. 父组件的 key 或 props 发生变化
    虽然你提到没有直接传递 props 更新给父组件,但如果父组件的 key 或其他 props(例如通过 context 或高阶组件传递的)发生变化,也可能导致组件重新渲染。
  2. Router 组件的位置
    如果 <Router> 组件(如 <BrowserRouter><HashRouter>)的位置不正确,或者嵌套过深,也可能影响路由的渲染行为。
  3. 组件的 shouldComponentUpdate
    如果父组件或其父级组件的 shouldComponentUpdate 方法被错误地实现,可能会阻止或允许不必要的渲染。
  4. React 的严格模式(StrictMode)
    如果你在应用的顶层使用了 <React.StrictMode>,它可能会故意触发组件的卸载和重新挂载,以帮助识别潜在的问题。

解决方案

  1. 检查 Router 组件的位置
    确保 <Router> 组件(如 <BrowserRouter>)是包裹在你整个应用的最外层,而不是嵌套在过深的组件结构中。
  2. 使用 React.memo 或 PureComponent
    如果父组件是纯组件(即其渲染输出仅依赖于其 props 和 state),你可以使用 React.memo(对于函数组件)或继承 React.PureComponent(对于类组件)来避免不必要的渲染。
  3. 检查 shouldComponentUpdate
    如果父组件或其父级组件有 shouldComponentUpdate 方法,请确保它正确实现了,以避免不必要的渲染。
  4. 移除 React.StrictMode
    如果你正在使用 <React.StrictMode>,尝试暂时移除它,看看问题是否仍然存在。
  5. 使用 React Router 的 withRouter HOC
    如果父组件需要访问路由信息(如 location, history 等),但又不希望因路由变化而重新渲染,你可以考虑使用 withRouter 高阶组件来包装父组件,但这通常不是解决渲染问题的首选方法。
  6. 检查其他可能影响渲染的库或代码
    确保没有其他库或代码(如 Redux、MobX 等状态管理库)在路由变化时无意中触发了父组件的更新。

示例代码调整

由于你的代码片段看起来结构上是合理的,你可能需要关注上述提到的其他潜在问题。如果问题依旧存在,你可能需要进一步检查父组件及其父级组件的实现。

如果你已经检查了上述所有可能的原因,并且问题仍然存在,请考虑创建一个简化的示例来复现这个问题,并在 Stack Overflow 或其他社区中寻求帮助。

1 个回答

这是因为 react-router 的 Switch 组件在匹配到新的路由时,会卸载之前匹配的组件并挂载新的组件。因此,当你切换子路由时,父组件会被卸载并重新挂载。
要解决这个问题,可以使用嵌套路由:将子路由放在父组件内部的 Switch 中,这样父组件不会因为子路由的变化而重新挂载。
例如:

<Col span={18}>
  <div className='c-d-contents-l'>
    <Menu onClick={this.handleClick} selectedKeys={[current]} mode="horizontal">
      <Menu.Item key="/course/courseDetails/courseCatalog">目录</Menu.Item>
      <Menu.Item key="/course/courseDetails/courseSetting">设置</Menu.Item>
      <Menu.Item key="/course/courseDetails/studentList">名单</Menu.Item>
    </Menu>
    <Switch>
      <Route path="/course/courseDetails" render={() => (
        <div>
          <Switch>
            <Route path="/course/courseDetails/courseCatalog" component={CourseCatalog} />
            <Route path="/course/courseDetails/courseSetting" component={CourseSetting} />
            <Route path="/course/courseDetails/studentList" component={StudentList} />
          </Switch>
        </div>
      )} />
    </Switch>
  </div>
</Col>

这样,父组件 /course/courseDetails 不会因为子路由的变化而重新挂载

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏