I have this router:
const router = createBrowserRouter(
[
{
path: '/',
element: <Navigate to={'/dashboards'}/>
},
{
path: '/dashboards',
element: <Dashboards/>,
loader: () => store.dispatch(retrieveWarehouses()),
children: [
{
path: ':warehouse',
element: <Dashboard/>,
loader: ({ params }) => store.dispatch(monitorWarehouse(params.warehouse))
}
]
}
]
)
Defined as is, the <Dashboard/>
component is not rendered, only its parent dashboard list (Dashboards
, notice the plural). The loader of the child Dashboard
is still triggered though.
If I don't use a nested route:
const router = createBrowserRouter(
[
{
path: '/',
element: <Navigate to={'/dashboards'}/>
},
{
path: '/dashboards',
element: <Dashboards/>,
loader: () => store.dispatch(retrieveWarehouses()),
},
{
path: '/dashboards/:warehouse',
element: <Dashboard/>,
loader: ({ params }) => store.dispatch(monitorWarehouse(params.warehouse))
}
]
)
The child component Dashboard
is rendered properly, but the loader of the parent is not triggered.
Here are the components:
Dashboards
const Dashboards: React.FC<any> = () => {
const {
warehouses,
loading
} = useAppSelector(selectWarehouseListState)
if (loading) {
return (
<div className={'warehouse-list'}>
<h1>Select warehouse</h1>
<Spinner/>
</div>
)
}
return (
<div className={'warehouse-list'}>
<h1>Select warehouse</h1>
{
warehouses.map((wh: Warehouse) => (
<NavLink to={`/dashboards/${wh.name}`} key={wh.name}>
<div className={'selectable-warehouse container'}>
{wh.name}
</div>
</NavLink>
))
}
</div>
)
}
Dashboard
const Dashboard: React.FC<any> = () => {
const { loading } = useAppSelector(selectWarehouseState)
const { warehouse } = useParams()
const dispatch = useAppDispatch()
useEffect(() => {
return () => {
dispatch(stopMonitorWarehouse(warehouse))
}
}, [dispatch])
if (loading) {
return (
<div className={'dashboard loading shrinkable'}>
<div className={'header'}>
<NavLink to={'/dashboards'} className={'nav-back'}>
<ArrowBack/>
</NavLink>
<div className={'selected-warehouse-name'}>{warehouse}</div>
</div>
<div className={'status connecting'}>status: connecting</div>
<Spinner/>
</div>
)
}
return (
<div className={'dashboard active shrinkable'}>
<div className={'header'}>
<NavLink to={'/dashboards'} className={'nav-back'}>
<ArrowBack/>
</NavLink>
<div className={'selected-warehouse-name'}>{warehouse}</div>
</div>
<div className={'status connected'}>status: connected</div>
<div className={'logs-metrics'}>
<Logs/>
</div>
</div>
)
}
How can I access /dashboards/foo
and trigger both loaders ?
If the Dashboards
component is rendered as a layout route then it necessarily should render an Outlet
for its nested routes to render their content into.
Example:
import { Outlet } from 'react-router-dom';
const Dashboards: React.FC<any> = () => {
const {
warehouses,
loading
} = useAppSelector(selectWarehouseListState)
return (
<div className={'warehouse-list'}>
<h1>Select warehouse</h1>
{loading ? (
<Spinner />
) : (
<>
{warehouses.map((wh: Warehouse) => (
<NavLink to={`/dashboards/${wh.name}`} key={wh.name}>
<div className={'selectable-warehouse container'}>
{wh.name}
</div>
</NavLink>
))}
<Outlet />
</>
)}
</div>
);
};
However in my use case I don't want both components to be rendered, the latter should take precedence of the other.
If I understand this part you want the Dashboards
and Dashboard
components rendered independently, but the Dashboards
's loader function to still be called even while on a nested route. For this you'll render Dashboards
as a nested index route where the loader function is on the parent layout route.
Example:
const router = createBrowserRouter(
[
{
path: '/',
element: <Navigate to="/dashboards" />
},
{
path: '/dashboards',
loader: () => store.dispatch(retrieveWarehouses()),
children: [
{
index: true,
element: <Dashboards />,
},
{
path: ':warehouse',
element: <Dashboard />,
loader: ({ params }) => {
store.dispatch(monitorWarehouse(params.warehouse));
},
}
]
}
]
);
The "/dashboards"
route will render an Outlet
by default when no element
is specified, and the Dashboards
component will be rendered when the parent route is matched. No changes to the Dashboards
component would be required.