REACT-ROUTER V4:如何避免在孩子路由失踪时渲染父路线



我正在尝试将大部分路线作为 AppShell组件的孩子,其中包含一个navbar。但是我想将我的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(没有孩子,但仍然是Navbar):

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

使用exactly使用数组pattern与根路由一起使用:

但是,我必须将所有可能的子路由放在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>

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

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

之类的事情

我一直在处理同一问题 - 我认为您可能想要这样的东西,一个'global 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'))

可能是我关于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 }
  ]} />
)

我提出了一个自定义的<Match>组件,这是一种可能的解决方案。不过,这远非达成共识的方式。

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

用法看起来像

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

最新更新