react-router v4:如何在子路径未命中时避免呈现父路由

时间:2016-12-01 18:08:14

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

我尝试将大部分路线渲染为AppShell组件的子组件,其中包含导航栏。但我想将我的404路由渲染为一个未包含在AppShell中的独立组件。

v2很容易:

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

一切都按预期运作:

  • /呈现<AppShell><Home /></AppShell>
  • /about呈现<AppShell><About /></AppShell>
  • /blah呈现<NotFound />

但我无法弄清楚如何使用v4:

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

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

有了这个:

  • /呈现<div><AppShell><Home /></AppShell></div>(好)
  • /about呈现<div><AppShell><About /></AppShell></div>(好)
  • /blah呈现<div><AppShell /><NotFound /></div> (问题 - 我想摆脱<AppShell />

如果没有根路由,则使用数组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>
)

使用带pattern的数组exactly可以使用根路由:

但是我必须在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>
)

但每次我过渡到回家路线时,这都会重新安装<AppShell>

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

如果我能做<Match exactlyPattern="/" pattern={["/about", "/contact"]} component={InAppShell} /> ...

之类的事情

3 个答案:

答案 0 :(得分:5)

我一直在解决同样的问题 - 我想你可能想要这样的东西,'全球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'))

答案 1 :(得分:1)

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

您应该将<AppShell>渲染为需要它的每个组件的容器。

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>
)

您还可以使用<MatchRoutes>组件。我更喜欢这个,因为它强迫相关路线组合在一起。

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

答案 2 :(得分:0)

我提出了一个自定义<Match>组件,这是一种可能的解决方案。尽管如此,它还远远不是一种商定的标准方式。

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>
)