【问题标题】:How does react-router keep components from unmounting with route changes?react-router 如何防止组件因路由更改而卸载?
【发布时间】:2020-08-01 09:07:39
【问题描述】:

我会先说一切正常,只是我试图了解到底发生了什么以及为什么我会看到这种行为。

假设我有一个组件FooComponent,以及两个不同的路由/foo/bar。我将它们渲染如下:

<Route
    exact
    path="/foo"
    render={({ location }) => <FooComponent location={location} />}
/>
<Route
    path="/bar"
    render={({ location }) => <FooComponent location={location} />}
/>

在页面/foo/bar 之间单击时,FooComponent 似乎只安装一次。为了测试这一点,下面是 FooComponent 的实现方式:

const FooComponent = ({ location }) => {
  useEffect(() => {
    console.log("Mount");
    return () => {
      console.log("Cleanup");
    };
  }, []);
  return <div>At {location.pathname}</div>;
};

Mount 只记录一次。

这完全让我感到困惑,因为 FooComponent 是由两个完全不同的父 Route 组件渲染的,我认为 React 只会对使用不同子元素重新渲染的同一个父组件执行协调。此外,将其中一个更改为返回 &lt;div&gt;&lt;FooComponent location={location} /&gt;&lt;/div&gt; 并保留另一个原样会导致组件卸载。

Here 是可重现的示例。

【问题讨论】:

  • 也许应该是 react-router-dom 的工作原理?

标签: reactjs react-router react-router-dom


【解决方案1】:

就像你提到的,我相信这种行为主要与 React Reconciliation 过程有关。每当根元素有不同的类型时,在我们的例子中是 &lt;div /&gt;&lt;FooComponent /&gt;,React 将创建一个新树。这就是我们看到卸载事件的原因。

在以下情况下,FooComponent 的 prop(位置)将在我们转到不同的路线时发生变化。因为我们有两个相同类型的 React DOM 元素,所以 React 只会更新和重新渲染组件。

// location is changed by react-route
<Route
    exact
    path="/foo"
    render={({ location }) => <FooComponent location={location} />}
/>
<Route
    path="/bar"
    render={({ location }) => <FooComponent location={location} />}
/>


const FooComponent = ({ location }) => {
  useEffect(() => {
    console.log("Mount");
    return () => {
      console.log("Cleanup");
    };
  }, []);
  
  // the parent components is the same, only the child node of the following div is chnaged, so React rerenders the div.
  return <div>At {location.pathname}</div>;
};

在 react-router 方面,Route 不会改变 DOM 结构,如果你查看源代码的话。它仅充当上下文提供者。

return (
            <RouterContext.Provider value={props}>
              {props.match
                ? children
                  ? typeof children === "function"
                    ? __DEV__
                      ? evalChildrenDev(children, props, this.props.path)
                      : children(props)
                    : children
                  : component
                  ? React.createElement(component, props)
                  : render
                  ? render(props)
                  : null
                : typeof children === "function"
                ? __DEV__
                  ? evalChildrenDev(children, props, this.props.path)
                  : children(props)
                : null}
            </RouterContext.Provider>
          );

参考资料:

react-dom/Router.js

React/Reconciliation

【讨论】:

    【解决方案2】:

    基本上我认为 React 优化了两个相等的无状态组件在计算 DOM 输出时等价的基本概念。 更改 key prop,您可以明确指定应重新安装组件:

    <Route exact path="/foo" render={({ location }) => <FooComponent key='1' location={location} />} />
    <Route path="/bar" render={({ location }) => <FooComponent key='2' location={location} />} />
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-05
      • 1970-01-01
      • 2023-01-10
      • 2018-02-24
      • 2019-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多