navi
navi copied to clipboard
How to combing views with shared component and other views
Some of my views share a navigation bar and some doesn't, how do I combine them?
For example, suppose my app have 3 pages, "home", "products" and "about", "home" and "products" have a shared navigation bar, but "about" doesn't.
From the docs I know that I can share component with withView:
withView((
<>
<NavBar />
<View />
</>
),
mount({
'/': route({
title: 'Home',
view: <Home />,
}),
'/products': route({
title: 'Products',
view: <Products />,
}),
})
)
But it's not clear how to add other routes that doesn't need the shared component.
I've tried to use compose, but it seemed mount can't be "composed"? I got 404 for "/about" with this code:
const shared = withView((
<>
<NavBar />
<View />
</>
),
mount({
'/': route({
title: 'Home',
view: <Home />,
}),
'/products': route({
title: 'Products',
view: <Products />,
}),
})
)
compose(
shared,
mount({
'/about': route({
title: 'About',
view: <About />,
}),
})
)
I've looked at the source code of frontarm.com/navi/, but I didn't find routes for "/learn" and "/articles" mentioned here, so I guess I missed something.
So how do I do it correctly?
The way to do this is with wildcards on the mount(). This lets you direct the mount to fall through to another matcher if it fails to match, which can then be wrapped with a withView():
mount({
'/about': lazy(() => import('./about')),
'*': compose(
withView(
<AppLayout>
<View />
</AppLayout>
),
mount({
'/home': lazy(() => import('./home')),
'/products': lazy(() => import('./products')),
})
)
})
The way to do this is with wildcards on the
mount(). This lets you direct the mount to fall through to another matcher if it fails to match, which can then be wrapped with awithView():mount({ '/about': lazy(() => import('./about')), '*': compose( withView( <AppLayout> <View /> </AppLayout> ), mount({ '/home': lazy(() => import('./home')), '/products': lazy(() => import('./products')), }) ) })
This solves the problem, but what if I have another page that shares a component with "/about" ?
That's a good question. I'm not sure I've got a great answer, but given that withView just returns a function, one possibility would be to create a layout, and apply it to each route individually:
const withMainLayout = (route) => compose(withView(...), route)
const withSecondaryLayout = (route) => compose(withView(...), route)
mount({
'/about': withSecondaryLayout(route(...)),
'/home': withMainLayout(route(...)),
'/products': withMainLayout(route(...)),
})
I've looked into making it so you can do compose(mount(), mount()) before, but from memory there was some issue that prevented it -- I think to it was related to the static build process. I'd definitely be open to PRs to make this easier though if it can be made to work with the build tools.
That's a good question. I'm not sure I've got a great answer, but given that
withViewjust returns a function, one possibility would be to create a layout, and apply it to each route individually:const withMainLayout = (route) => compose(withView(...), route) const withSecondaryLayout = (route) => compose(withView(...), route) mount({ '/about': withSecondaryLayout(route(...)), '/home': withMainLayout(route(...)), '/products': withMainLayout(route(...)), })I've looked into making it so you can do compose(mount(), mount()) before, but from memory there was some issue that prevented it -- I think to it was related to the static build process. I'd definitely be open to PRs to make this easier though if it can be made to work with the build tools.
Thanks for your support! I've thought of the "with" method, but composing mount is more natural and concise, I'd like to find out how to do it when I have time.