molefrog / wouter

🥢 A minimalist-friendly ~2.1KB routing for React and Preact
https://npm.im/wouter
The Unlicense
6.59k stars 150 forks source link

Support nested Switches #467

Open mnieber opened 1 month ago

mnieber commented 1 month ago

Hi, I've read the docs to see if nested Switches are supported (a longstanding wish), and I think they are not, so I wanted to make a feature proposal.

The idea is that a switch picks the first matching route or switch. A switch matches if one of its routes (or switches) matches. It's basically the Composite pattern.

The use-case is this one:

<Switch>
    <Route path="blue/one"/>
    <Route path="red/one"/>
    <Route path="bread/one"/>
    <Route path="apple/one"/>
</Switch>

If I want to create separate route groups for "color" or "food" then it would be great if I can refactor this to

// Switch that renders either ColorsSwitch or FoodsSwitch.
// This approach can be used to clean up switches with many routes.
// It can also be used to reuse switches in different locations.
<Switch>
    <ColorsSwitch/>
    <FoodsSwitch/>
</Switch>

const ColorsSwitch = () => {
    return (
        <Switch>
            <Route path="blue/one"/>
            <Route path="red/one"/>
        </Switch>
    )
}

const FoodsSwitch = () => {
    return (
        <Switch>
            <Route path="bread/one"/>
            <Route path="apple/one"/>
        </Switch>
    )
}
schmitch commented 1 month ago

yeah I had the same problem that switch does not work well with nested routes like:

function ComponentTreeBRouter() {
return       <><Route path="/">
        <Redirect to="/list"/>
      </Route>

      <Route path="/list">
        <ComponentC/>
      </Route>
      <Route path="/overview/:id">
        {params => <ComponentD/>}
      </Route></>;
}
<Router base="...">
           <Route path="/component-tree-a" nest>
          <>
            <Route path="/">
              <ComponentA/>
            </Route>
            <Route path="/config">
              <ComponentB/>
            </Route>
          </>
        </Route>

        <Route path="/component-tree-b" nest>
          <ComponentTreeBRouter/>
        </Route>

        <Route><Error404RouteComponent/></Route>
      </Switch>
</Router>

actually I created a NestedRouter which I wrapped inside the nested routes, like:

function EvRouter(props: { children: ReactNode }) {
  return (<Switch>
    {props.children}
    <Route><Error404RouteComponent/></Route>
  </Switch>);
}

switch than looks like:

<Router base="...">
           <Route path="/component-tree-a" nest>
          <EvRouter>
            <Route path="/">
              <ComponentA/>
            </Route>
            <Route path="/config">
              <ComponentB/>
            </Route>
          </EvRouter>
        </Route>

        <Route path="/component-tree-b" nest>
          <ComponentTreeBRouter/> <!-- also has EvRouter inside as the first component -->
        </Route>

        <Route><Error404RouteComponent/></Route>
      </Switch>
</Router

I would prefer if something like:

<Switch>
      <ComponentTreeARouter />
      <ComponentTreeBRouter />
      <Route><Error404RouteComponent/></Route>
</Switch>

but looks like that might be hard to support...

mnieber commented 1 month ago

@schmitch It's a bit hard to follow what your problem with nested routes is. Can you give an example of the thing that doesn't work for you, but only using Switch and Route? (and no custom components). Or is it (exactly) the same problem as what I posted?

schmitch commented 1 month ago

@mnieber actually the custom components is the problem.

we prefer them to make nested routes since we have tons of routes

haggen commented 1 month ago

I need something like this so that some routes share a context provider.

// App.jsx
<Switch>
  <Route path="/a" component={A} nest />
  <Route path="/b" component={B} />
</Switch>

// A.jsx
<Provider>
  <Switch>
    <Route path="/1" component={A1} />
    <Route path="/2" component={A2} />
  </Switch>
</Provider>
mnieber commented 1 month ago

@haggen It looks like your example would already work with the current Switch. Doesn't it work?

haggen commented 1 month ago

@mnieber Oh indeed it does! Reading the docs I was under the impression that Routes needed to be directly nested. Thanks!