preactjs / preact-router

:earth_americas: URL router for Preact.
http://npm.im/preact-router
MIT License
1.01k stars 156 forks source link

Layout using preact-router ? #31

Closed crapthings closed 8 years ago

crapthings commented 8 years ago

is this same to react router ?

developit commented 8 years ago

Hmm - not sure what you mean. You can use nested routes and they will be resolved the same as any component:

<Router>
  <div path="/1">
    <Router>
      <div path="/*/a">One A</div>
      <div path="/*/b">One B</div>
    </Router>
  </div>
  <div path="/2">
    <Router>
      <div path="/*/a">Two A</div>
      <div path="/*/b">Two B</div>
    </Router>
  </div>
</Router>
developit commented 8 years ago

I think I'm going to close this for now, since I don't want to re-implement react-router, but rather provide a simple mapping of the browser's URL to props with pattern matching. react-router is a great project, and I would rather people who need that level of flexibility just use it directly (it works with preact-compat) to benefit from all of @mjackson, @ryanflorence & co's work.

pearofducks commented 8 years ago

@crapthings - i'm a bit late to answer here, but this is how you'd convert a react-router layout to preact-router:

react-router

<Router history={browserHistory}>
  <Route path='/' component={Layout}>
    <IndexRoute component={RecipeList} />
    <Route path="/:name" component={Recipe} />
  </Route>
</Router>

preact-router

<Layout>
  <Router>
    <RecipeList path="/" />
    <Recipe path="/:name" />
  </Router>
</Layout>
developit commented 8 years ago

@pearofducks thanks for the example, I think that'll be useful for anyone finding this issue from Google. Cheers!

crapthings commented 8 years ago

@pearofduck, @developit

this is it ! thanks~

tooxie commented 3 years ago

Either things have changed since 2016 or I didn't understand how to implement it, but @pearofducks' solution did not work for me for a few reasons, namely:

What worked for me was to decorate the components with their respective layouts, for example:

// src/components/router.tsx
import { FunctionalComponent, h } from "preact";
import { Route, Router } from "preact-router";

import Recipe from "../routes/recipe";
import NotFoundPage from "../routes/notfound";
import Collection from "./layout/collection";
import Detail from "./layout/detail";

export default (() => {
  const RecipeCollection = Collection(Recipe);
  const RecipeDetail = Detail(Recipe);

  return (
    <Router>
      <Route path="/recipes/" component={RecipeCollection} />
      <Route path="/recipes/:slug" component={RecipeDetail} />

      <NotFoundPage default />
    </Router>
  );
}) as FunctionalComponent;

Then the decorators are a simple function that receives a component and returns it decorated with the corresponding layout:

// src/components/layout/detail/index.tsx
import { FunctionalComponent, h } from "preact";
import DetailLayout from "./component";

export default (Component: FunctionalComponent): FunctionalComponent => {
  return props => (
    <DetailLayout>
      <Component {...props} />
    </DetailLayout>
  );
};

The same for the collection:

// src/components/layout/collection/index.tsx
import { FunctionalComponent, h } from "preact";
import CollectionLayout from "./component";

export default (Component: FunctionalComponent): FunctionalComponent => {
  return props => (
    <CollectionLayout>
      <Component {...props} />
    </CollectionLayout>
  );
};

Since this is the only reference I could find to this problem for preact specifically I leave my solution here. I hope this helps anyone looking to achieve this without having to resort to preact-compat. I'm very new to preact so I would really appreciate feedback on this approach.

dolie commented 1 year ago

Here is a workaround using preact signals (see below). Layout does not need to be wrapped into Router, can use multiple (also nested) Layouts

export const url = signal('')
import { url } from './store/router';

function App(){
   function handleRoute({ url: _url }: { url: string }){
     url.value = _url
   }

   return (
     <>
       <Layout>
         <Router onChange={e => handleRoute(e)}>
           <Route path="/search" component={Search}/>
           <Route path="/profile" component={Profile}/>
         </Router>
       </Layout>
     </>
   )
 }
 import { url } from './store/router';

 function Layout({children}){
   return (
     { url.value? === '/search' && (
       <div>
         Search layout
         <div>{children}<div>
       </div>
      }
     { ['/profile', '/home'].includes(url.value) && (
       <div>
         Profile and home layout
         <div>{children}<div>
       </div>
     }
   )
 }