【问题标题】:react-router v4: how to avoid rendering parent routes when child routes missreact-router v4:当子路由丢失时如何避免渲染父路由
【发布时间】:2017-04-16 11:56:36
【问题描述】:

我正在尝试将我的大部分路线呈现为 AppShell 组件的子组件,该组件包含一个导航栏。但是我想将我的 404 路由呈现为一个独立的组件,而不是包装在 AppShell 中。

使用 v2 很容易:

<Router>
  <Route component={AppShell}>
    <Route path="/about" component={About} />
    <Route path="/" component={Home} />
  </Route>
  <Route path="*" component={NotFound} />
</Router>

一切正常:

  • / 渲染 &lt;AppShell&gt;&lt;Home /&gt;&lt;/AppShell&gt;
  • /about 渲染 &lt;AppShell&gt;&lt;About /&gt;&lt;/AppShell&gt;
  • /blah 渲染 &lt;NotFound /&gt;

但我不知道如何使用 v4:

现在我正在这样做,但问题是它呈现AppShell(没有孩子,但仍然是导航栏):

const Routes = () => (
  <div>
    <AppShell>
      <Match exactly pattern="/" component={Home} />
      <Match pattern="/about" component={About} />
    </AppShell>
    <Miss component={NotFound} />
  </div>
)

有了这个:

  • / 渲染 &lt;div&gt;&lt;AppShell&gt;&lt;Home /&gt;&lt;/AppShell&gt;&lt;/div&gt;(好)
  • /about 渲染 &lt;div&gt;&lt;AppShell&gt;&lt;About /&gt;&lt;/AppShell&gt;&lt;/div&gt;(好)
  • /blah 渲染&lt;div&gt;&lt;AppShell /&gt;&lt;NotFound /&gt;&lt;/div&gt; (问题——我想摆脱&lt;AppShell /&gt;

如果没有根路由,则使用数组 pattern 有效:

const InAppShell = (): React.Element<any> => (
  <AppShell>
    <Match pattern="/about" component={About} />
    <Match pattern="/contact" component={Contact} />
  </AppShell>
)

const App = (): React.Element<any> => (
  <div>
    <Match pattern={['/contact', '/about']} component={InAppShell} />
    <Miss component={NotFound} />
  </div>
)

使用patternexactly 的数组与根路由一起使用:

但是我必须将所有可能的子路由放在pattern数组中...

const InAppShell = (): React.Element<any> => (
  <AppShell>
    <Match exactly pattern="/" component={Home} />
    <Match pattern="/about" component={About} />
  </AppShell>
)

const App = (): React.Element<any> => (
  <div>
    <Match exactly pattern={["/", "/about"]} component={InAppShell} />
    <Miss component={NotFound} />
  </div>
)

但在一个包含大量路由的大型应用程序中,这将是相当笨拙的。

我可以为/ 单独创建一个Match

const InAppShell = (): React.Element<any> => (
  <AppShell>
    <Match exactly pattern="/" component={Home} />
    <Match pattern="/about" component={About} />
    <Match pattern="/contact" component={Contact} />
  </AppShell>
)

const App = (): React.Element<any> => (
  <div>
    <Match exactly pattern="/" component={InAppShell} />
    <Match pattern={["/about", "/contact"]} component={InAppShell} />
    <Miss component={NotFound} />
  </div>
)

但是,每次我往返于家乡路线时,这都会重新安装&lt;AppShell&gt;

这里似乎没有理想的解决方案;我认为这是 v4 需要解决的基本 API 设计挑战。

如果我能做类似&lt;Match exactlyPattern="/" pattern={["/about", "/contact"]} component={InAppShell} /&gt;...

【问题讨论】:

  • 你解决了吗?我有完全相同的问题。

标签: http-status-code-404 react-router nested-routes


【解决方案1】:

我一直在解决同样的问题 - 我想你可能想要这样的东西,一个“全局 404”:

https://codepen.io/pshrmn/pen/KWeVrQ

const {
  HashRouter,
  Route,
  Switch,
  Link,
  Redirect
} = ReactRouterDOM

const Global404 = () => (
  <div>
    <h1>Oh, no!</h1>
    <p>You weren't supposed to see this... it was meant to be a surprise!</p>
  </div>
)

const Home = () => (
  <div>
    The links to "How?" and "Random" have no matching routes, so if you click on either of them, you will get a "global" 404 page.
  </div>
)
const Question = ({ q }) => (
  <div>
    <div>Question: {q}</div>
    <div>Answer: I have no idea</div>
  </div>
)
const Who = () => <Question q={"Who?"}/>
const What = () => <Question q={"What?"}/>
const Where = () => <Question q={"Where?"}/>
const When = () => <Question q={"When?"}/>
const Why = () => <Question q={"Why?"}/>

const RedirectAs404 = ({ location }) => 
  <Redirect to={Object.assign({}, location, { state: { is404: true }})}/>

const Nav = () => (
  <nav>
    <ul>
      <li><Link to='/'>Home</Link></li>
      <li><Link to='/faq/who'>Who?</Link></li>
      <li><Link to='/faq/what'>What?</Link></li>
      <li><Link to='/faq/where'>Where?</Link></li>
      <li><Link to='/faq/when'>When?</Link></li>
      <li><Link to='/faq/why'>Why?</Link></li>
      <li><Link to='/faq/how'>How?</Link></li>
      <li><Link to='/random'>Random</Link></li>
    </ul>
  </nav>
)

const App = () => (
  <Switch>
    <Route exact path='/' component={Home}/>
    <Route path='/faq' component={FAQ}/>
    <Route component={RedirectAs404}/>
  </Switch>
)

const FAQ = () => (
  <div>
    <h1>Frequently Asked Questions</h1>
    <Switch>
      <Route path='/faq/who' component={Who}/>
      <Route path='/faq/what' component={What}/>
      <Route path='/faq/where' component={Where}/>
      <Route path='/faq/when' component={When}/>
      <Route path='/faq/why' component={Why}/>
      <Route component={RedirectAs404}/>
    </Switch>
  </div>
)

ReactDOM.render((
  <HashRouter>
    <div>
      <Nav />
      <Route render={({ location }) => (
        location.state && location.state.is404
          ? <Global404 />
          : <App />
      )}/>
    </div>
  </HashRouter>
), document.getElementById('root'))

【讨论】:

  • 这真是天才……我花了一段时间才明白这是多么天才……
  • 这与在整个应用中的每个 Switch 中添加诸如 之类的内容有何不同?
  • 不同之处在于,这会将404页面呈现在根组件上,而不是Switch上,见接近尾声的代码location.state &amp;&amp; location.state.is404 ? &lt;Global404 /&gt; : &lt;App /&gt;
【解决方案2】:

可能我最不喜欢 v4 的一点是,应该组合在一起的匹配和未命中可以放在组件树中的不同级别。这会导致像您这样的情况,您有一个只应为某些匹配呈现的组件,但多级匹配结构允许您在其中嵌套匹配。

您应该将&lt;AppShell&gt; 渲染为每个需要它的组件的容器。

const Home = (props) => (
  <AppShell>
    <div>
      <h1>Home</h1>
    </div>
  </AppShell>
)

const About = (props) => (
  <AppShell>
    <div>
      <h1>About</h1>
    </div>
  </AppShell>
)

const App = () => (
  <div>
    <Match exactly pattern='/' component={Home} />
    <Match pattern="/about" component={About} />
    <Miss component={NotFound} />
  </div>
)

您也可以使用&lt;MatchRoutes&gt; 组件。我更喜欢这个,因为它强制将相关路由组合在一起。

const App = () => (
  <MatchRoutes missComponent={NotFound} routes={[
    { pattern: '/', exact: true, component: Home },
    { pattern: '/about', component: About }
  ]} />
)

【讨论】:

  • 不幸的是,每次路由更改时都会重新安装AppShell。不过我不知道MatchRoutes,也许我可以让它工作得足够好......
  • 如果我对和解的理解是正确的,&lt;MatchRoutes&gt; &lt;AppShell&gt; 不应该被卸载和重新安装。
  • 好的。我希望我能找到一种使用&lt;MatchRoutes&gt; 的方法,而无需将每个组件单独包装在&lt;AppShell&gt;...
  • 如果最坏的情况发生,我可以编写自己的组件,对位置进行必要的匹配
  • 好的,是的,查看MatchRoutes 的代码,我发现它不会重新挂载&lt;AppShell&gt;。我还可以轻松地制作一个组件,在将组件包装在&lt;AppShell&gt; 中的每条路由上呈现&lt;MatchRoutes&gt;render
【解决方案3】:

我想出了一个自定义的&lt;Match&gt; 组件,这是一种可能的解决方案。不过,这远不是一种公认​​的标准做事方式。

https://gist.github.com/jedwards1211/15140b65fbeafcbc14dec728fee16f59

用法看起来像

const InAppShell = (): React.Element<any> => (
  <AppShell>
    <Match exactPattern="/" component={Home} />
    <Match pattern="/about" component={About} />
    <Match pattern="/contact" component={Contact} />
  </AppShell>
)

const App = (): React.Element<any> => (
  <div>
    <Match exactPattern="/" patterns={["/about", "/contact"]} register={false} component={InAppShell} />
    <Match notExactPattern="/" notPatterns={["/about", "/contact"]} register={false} component={NotFound} />
  </div>
)

【讨论】:

    猜你喜欢
    • 2018-10-18
    • 1970-01-01
    • 2018-02-02
    • 1970-01-01
    • 2023-03-12
    • 2018-06-06
    相关资源
    最近更新 更多