I am starting react project with react-router-dom version 6.4.0, but not able to create common header for all the routes.
App.js - This is the file where I am adding RouterProvider
import logo from './logo.svg';
import './App.css';
import { Link, Outlet, RouterProvider } from "react-router-dom";
import { routers } from "./routes/routes";
function App() {
return (
<RouterProvider router={routers}>
<div>Header</div>
<Outlet />
</RouterProvider>
);
}
export default App;
routes.js - In this file I am going to create all the routes
import { createBrowserRouter, redirect } from "react-router-dom";
import About from "../pages/About/About";
import Home from "../pages/Home/Home";
import { getUser, isAuthenticated } from "../sso/authUtil";
const authLoader = () => {
if (!isAuthenticated()) {
return redirect("https://google.com");
} else {
return getUser();
}
};
export const routers = createBrowserRouter([
{
path: "/",
element: <Home />,
loader: authLoader,
},
{
path: "/about",
element: <About />,
},
]);
Home.js - This file is home page which will have the links to other pages along with header
import React from "react";
import { Link, Outlet, useLoaderData } from "react-router-dom";
const Home = () => {
const loaderData = useLoaderData();
return (
<>
<div>Header</div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<div>Home: {loaderData}</div>{" "}
<Outlet />
</>
);
}
export default Home;
About.js - It is normal component which say about
import React from "react";
const About = () => {
return <div>About</div>;
};
export default About;
I want both Home and About component to be loaded with header on top.
Even in react-router-dom#6.4.0 rendering layout routes with common UI still works the same.
Create a layout route component that render the common header component and an Outlet for nested routes.
Example:
const Layout = () => (
<>
<CommonHeader />
<Outlet />
</>
);
Import and render Layout on a layout route in the configuration.
const routers = createBrowserRouter([
{
element: <Layout />,
children: [
{
path: "/",
element: <Home />,
loader: authLoader
},
{
path: "/about",
element: <About />
}
]
}
]);
...
function App() {
return <RouterProvider router={routers} />;
}
Related
I don't know why my other component is not rendering when I press save, except Dashboard. When I re-run rpm run dev the component is not working, it's rendering and saving. I want to real-time when I save code.
Here is the dashboard when I press save, it's rendering the text:
import React from "react"
export default function Dashboard() {
return <div>admin dashboard test rendering</div>
}
Here is the component not rendering and not saving when I save:
import React from "react"
export default function Profile() {
return <div> not rendering when i save Profile test</div>
}
Here is my router
import { createBrowserRouter } from 'react-router-dom'
import NotFound from './NotFound'
import AdminLayout from './components/AdminLayout'
import Dashboard from './views/admin/dashboard'
import Profile from './views/admin/Profile'
const router = createBrowserRouter([
{
path: '/admin',
element: <AdminLayout />,
children: [
{
path: '/admin/dashboard',
element: <Dashboard />
},
{
path: '/admin/profile',
element: <Profile />
},
]
},
{
path: '/*',
element: <NotFound />
}
])
Here is my parent route adminlayout
import { Navigate, Outlet, NavLink } from "react-router-dom"
import { useStateContext } from "../contexts/ContextProvider"
import axiosClient from "../axios-client"
export default function AdminLayout() {
const { setToken, setUser } = useStateContext()
function onLogOut() {
axiosClient.post('/logout').
then(() => {
setUser({})
setToken(null)
})
}
return (
<div id="defaultLayout">
<aside>
<NavLink to="/admin/dashboard">Dashboard</NavLink>
<NavLink to="/admin/profile">Profile</NavLink>
<NavLink to="/" onClick={onLogOut}>Logout</NavLink>
</aside>
<div className="content">
<header>
<div>header</div>
</header>
<main>
<Outlet />
</main>
</div>
</div>
)
}
I don't know what is the problem of my code maybe Vite or I coded the wrong code.
Description - I made a Route.tsx file where I would be storing all of my routes, into the app, including the App.tsx (at path: '/') itself.
Here's my Route.tsx file - also (Approach 1)
import { lazy } from "react";
import { createBrowserRouter, Navigate, RouteObject } from "react-router-dom";
import App from "../layout/App";
// react - lazy paint
const Home = lazy(() => import("../pages/home/Home"));
const Toys = lazy(() => import("../pages/products/Toys"));
const Books = lazy(() => import("../pages/learning/Learning"));
const NotFound = lazy(() => import("../pages/notFound/NotFound"));
export const routes : RouteObject[] = [
{
path: '/',
element: <App />,
children: [
{ path: "toys", element: <Toys /> },
{ path: "/", element: <Home /> },
{ path: "books", element: <Books /> },
{ path: "not-found", element: <NotFound /> },
{ path: "*", element: <Navigate replace to = {"/not-found"} /> }
]
}
];
// export all routes
export const router = createBrowserRouter(routes);
These all above mentioned routes are meant to wrapped inside of a container (from Material UI). Whilst, there is one page, which I wish not to enclose into the container, named Cars.tsx, so I modified my App.tsx file like the one mentioned below:
App.tsx file
import { Container, CssBaseline, ThemeProvider } from "#mui/material";
import { Fragment, lazy, Suspense } from "react";
import { Outlet, Route, Routes } from "react-router-dom";
import AppLoad from "./AppLoad";
import Header from "./Header";
// react - lazy imports
const Cars = lazy(() => import("../pages/cars"));
function App() {
return (
<Fragment>
<ThemeProvider theme={theme}>
<CssBaseline />
<Header />
// for lazy paint load ...
<Suspense fallback={<AppLoad message={'Please wait. Loading ...'} />}>
<Routes>
<Route path="cars" element={<Cars />} />
// this particular page, I don't want to enclose it into
// the container, because it contains a hero image.
</Routes>
<Container maxWidth={'xl'}>
<Outlet /> // all the rest of pages are inside of container
</Container>
</Suspense>
</ThemeProvider>
</Fragment>
);
}
export default App;
Problem Description - Everything is working fine, only problem I am facing is when I am at "/cars" page, at the very bottom of the page, it is also rendering "Not-Found" (previously it was solved by <Switch /> in react-router-dom's version < 6).
Expected Behavior - I don't want to see "Not Found" message at the very bottom of "Cars" page. How to fix it?
I know, the fix would be very simple, but I couldn't figure it out.
Also, I would like to ask whether or not the above mentioned approach is better (like creating a separate file for Route, and then using it as <Outlet />), or the traditional approach is better where we have as.
Approach 2
<Routes>
<Route path='page1' element={<Page1 />} />
<Route path='page2' element={<Page2 />} />
<Route path='page1' element={<Page3 />} />
</Routes>
Would it be going to make any difference into the First content-ful paint loading of any routed page so far if I work with any of the approach as mentioned above?
The issue it seems is that the App component is rendering both an Outlet for nested routes and a Routes component for the "/cars" descendent route. When the path is "/cars" the App component is rendered and renders the matching "/cars" descendent route is rendered, but there is no matching nested "/cars" route so it looks like the NotFound route/component is additionally rendered in the Outlet.
If the goal is to not render the "/cars" route and Cars component inside the Container then I'd recommend creating an additional layout route and conditionally render the nested routes into the appropriate layout routes.
Example:
import { Container } from "#mui/material";
import { Outlet } from 'react-router-dom';
const ContainerLayout = () => (
<Container maxWidth="xl">
<Outlet />
</Container>
);
routes.tsx
import { lazy } from "react";
import { createBrowserRouter, Navigate, RouteObject } from "react-router-dom";
import App from "../layout/App";
// react - lazy paint
const Home = lazy(() => import("../pages/home/Home"));
const Toys = lazy(() => import("../pages/products/Toys"));
const Books = lazy(() => import("../pages/learning/Learning"));
const NotFound = lazy(() => import("../pages/notFound/NotFound"));
const Cars = lazy(() => import("../pages/cars"));
export const routes : RouteObject[] = [
{
path: '/',
element: <App />,
children: [
{
element: <ContainerLayout />,
children: [
{ index: true, element: <Home /> },
{ path: "toys", element: <Toys /> },
{ path: "books", element: <Books /> },
{ path: "not-found", element: <NotFound /> }
],
},
{ path: "cars" element: <Cars /> },
{ path: "*", element: <Navigate replace to="/not-found" /> }
]
},
];
// export all routes
export const router = createBrowserRouter(routes);
App.tsx
import { CssBaseline, ThemeProvider } from "#mui/material";
import { Suspense } from "react";
import { Outlet } from "react-router-dom";
import AppLoad from "./AppLoad";
import Header from "./Header";
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Header />
// for lazy paint load ...
<Suspense fallback={<AppLoad message="Please wait. Loading ..." />}>
<Outlet />
</Suspense>
</ThemeProvider>
);
}
export default App;
react-router-dom#6.4.1 is not rendering components.
I haven't coded React in a while now. I am trying to utilize react-router but something seems to be wrong. There's no error emitted but the components won't render. So many updates to react-router, it tough to keep up with.
Link to github repo
This is a sample of my root file.
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import './index.css';
import {
createBrowserRouter,
RouterProvider,
Route,
} from "react-router-dom";
import { Calendar, Message, Settings, Team } from './components';
import ErrorPage from "./error-page";
import Aside from './root';
const router = createBrowserRouter([
{
path: "/",
element: <Aside />,
errorElement: <ErrorPage />,
children: [
{
path: "/messages",
element: <Message />
},
{
path: "/team-members",
element: <Team />
},
{
path: "/calendar",
element: <Calendar />
},
{
path: "/settings",
element: <Settings />
}
]
}
]);
const container = document.getElementById('root')!;
const root = createRoot(container);
root.render(
<React.StrictMode>
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
</React.StrictMode>
);
Check your CSS layout, your components are rendered, but they are at the bottom of the page.
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Outlet />
</div>
);
}
You can read it from here:
https://reactrouter.com/en/main/components/outlet
Today I faced the same trouble and here is solution:
Go to your main Container, where you want to see children.
then
import { Link, Outlet } from 'react-router-dom';
then
inser <Outlet /> component, in <div> where you want to render items.
Your code: If you want to render <Team/> you should import <Outlet/> to component where you want to see <Team/>.
import React from 'react';
import { Link, Outlet } from 'react-router-dom';
const Aside = () => {
return (
<div>
Welcome to Your personal gym
<Link to='/login'>Log In</Link>
<Link to='/registration'>Sign In</Link>
<Outlet/>
</div>
);
}
export default Aside;
I am trying to set up my project for the first time using useRoutes and lazy loading with children. But I am having a problem that it won't load the children when redirected to that site? The Home page works perfectly fine.
https://codesandbox.io/s/great-rgb-ippuob?file=/src/Page/Home/index.js
Home path="/"
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
return (
<>
<h1>Hello Welcome to my beauty page</h1>
<Link to="/1">click me</Link>
</>
);
};
export default Home;
Children:
import React from "react";
import { useParams } from "react-router-dom";
const Id = () => {
const { id: loadingId } = useParams();
return <h1>Right now you are on the side of: {loadingId}</h1>;
};
export default Id;
App.js:
import "./styles.css";
import Route from "./Routes";
import { Suspense } from "react";
export default function App() {
return (
<>
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<Route />
</Suspense>
</div>
</>
);
}
Routes:
import { useRoutes } from "react-router-dom";
import React from "react";
export default function Router() {
return useRoutes([
{
path: "/",
element: <Home />,
children: [{ path: "/:id", element: <Id /> }]
}
]);
}
const Home = React.lazy(() => import("./Page/Home"));
const Id = React.lazy(() => import("./Page/Id"));
And in Index I have
<BrowserRouter>
<App />
</BrowserRouter>
You should add <Outlet /> in Layout/Parent component (Home for you)
const Home = () => {
return (
<>
<h1>Hello Welcome to my beauty page</h1>
<Link to="/1">click me</Link>
<Outlet />
</>
);
};
export default Home;
On my attempt to do nested routes, I've failed to have the child components to mount when the route changes through Link or history.push; but if declaring the routes directly in the root.js file, it works. So, ideally I'd like to keep as much routes configuration as possible in the root/routes.js file and not all over the App (I'm iterating over the root/routes.js object instead to do this automatically; I mean... trying)
To break it down logically (it's a bit abstract, but check the code below afterwards please):
- There's a `root/routes.js` that has all the routes configuration (parents, nested components, etc)
- The `root.js` defines the `<Route...>` where the attribute `component` is the return value of a function that passes the `routes` configuration to its `routes` component prop
- the main wrapper iterates over the component prop `routes` and defines `child` routes automatically...I mean...I'm trying...
Why would I want to do this? The way my brain works and why not? Was possible before react router 4
<MyAppWrapper>
<CommonNavigationBar />
<Main>
----- dynamic / changes by route etc -----
</Main>
<Footer />
</MyAppWrapper>
I wonder where my attempt is failing?
// Working version
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
import App from '../app/containers/app'
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<Route path='/' component={App} />
</BrowserRouter>
</Provider>
)
}
export default Root
For the previous example, the App component as nested , bellow I'm trying to do that dynamically..and it fails for some reason! It should be exactly the same though...there must be a typoe somewhere...
like,
import React, { Component } from 'react'
import { isBrowser } from 'reactatouille'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { withRouter, Route } from 'react-router'
import Navbar from '../navbar'
import JourneySelector from '../journeySelector'
import reservationFinder from '../../../reservationFinder'
// include the stylesheet entry-point
isBrowser() && require('../../../../sass/app.scss')
class App extends Component {
constructor (props) {
super(props)
this.state = {
init: true
}
}
render () {
return (
<div className={'app' + ' ' + (!this.state.init && 'uninitialised')}>
<Navbar />
<main>
<Route exact path='/' component={JourneySelector} />
<Route exact path='/reservation-finder' component={reservationFinder.containers.App} />
</main>
</div>
)
}
}
// export default App
function mapStateToProps (state, ownProps) {
return {
// example: state.example
}
}
function matchDispatchToProps (dispatch) {
return bindActionCreators({
// replay: replay
}, dispatch)
}
export default connect(mapStateToProps, matchDispatchToProps)(withRouter(App))
While my technique fails (all I'm trying to do is iterate the root/routes children routes to generate these ):
// root/routes.js
import app from '../app'
import reservationFinder from '../reservationFinder'
const rootRoutes = [
{
path: '/',
component: app.containers.App,
exact: true,
routes: [{
path: '/',
exact: true,
component: app.containers.JourneySelector
}, {
path: '/reservation-finder',
component: reservationFinder.containers.App
}]
}
]
export default rootRoutes
The root js file. You see the setRoute fn returns a new component, where the children routes is passed as a props? I believed this would work:
// root.js
import React from 'react'
import { Route, Switch } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
const setRoute = (route) => {
const MyComponent = route.component
return <Route key={route.path} exact={route.exact || false} component={() => (<MyComponent routes={route.routes} />)} />
}
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
{ rootRoutes.map(route => setRoute(route)) }
</BrowserRouter>
</Provider>
)
}
export default Root
the main app that I want to use as a wrapper:
// main app
import React, { Component } from 'react'
import { isBrowser } from 'reactatouille'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { withRouter, Route } from 'react-router'
import Navbar from '../navbar'
// include the stylesheet entry-point
isBrowser() && require('../../../../sass/app.scss')
class App extends Component {
constructor (props) {
super(props)
this.state = {
init: true
}
}
render () {
return (
<div className={'app' + ' ' + (!this.state.init && 'uninitialised')}>
<Navbar />
<main>
{ Array.isArray(this.props.routes) && this.props.routes.map(route => <Route key={route.path} {...route} />) }
</main>
</div>
)
}
}
// export default App
function mapStateToProps (state, ownProps) {
return {
// example: state.example
}
}
function matchDispatchToProps (dispatch) {
return bindActionCreators({
// replay: replay
}, dispatch)
}
export default connect(mapStateToProps, matchDispatchToProps)(withRouter(App))
I understand I MIGHT be able to achieve something similar, like?!
// root
import React from 'react'
import { Route, Switch } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
import MyAppWrapper from 'xxx/MyAppWrapper'
const setRoute = (route) => {
const MyComponent = route.component
return <Route key={route.path} exact={route.exact || false} component={() => (<MyComponent routes={route.routes} />)} />
}
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<MyAppWrapper>
<Route path='x' component={x} />
<Route path='y' component={y} />
</MyAppWrapper>
</BrowserRouter>
</Provider>
)
}
export default Root
Notes: During testing, I've noticed that it worked server-side? I mean, I may have missed something, and I didn't save my work. Also, when it fails, the previous component (the default) is still mounted and does not unmount
I even tried (without sucess...I wonder if this is a bug):
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import App from '../app/containers/app'
import rootRoutes from './routes'
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<Route path='/' render={() => (
<App />
)} />
</BrowserRouter>
</Provider>
)
}
export default Root
Ok, I think this is a bug so reported ( https://github.com/ReactTraining/react-router/issues/5190 ), you can find the live example here ( https://codepen.io/helderoliveira/pen/rmXdgd ), click topic. Maybe what I'm trying to do is not supported, but instead of blank we should get an error message.
Ok, I found the typo. The solution is to use render and pass the routerProps + any other props you desire through Object.assign and the spread operator!
// root.js
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import App from '../app/containers/app'
import rootRoutes from './routes'
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<Route path='/' render={routeProps => <App {...Object.assign({}, routeProps, { routes: rootRoutes[0].routes })} />} />
</BrowserRouter>
</Provider>
)
}
export default Root
And the main app wrapper:
class App extends Component {
render () {
return (
<div className={'app kiosk' + ' ' + (!this.state.init && 'uninitialised')}>
<Navbar />
<main>
{ Array.isArray(this.props.routes) && this.props.routes.map(route => <Route key={route.path} exact={route.exact} path={route.path} component={route.component} />) }
</main>
</div>
)
}
}
export default App
the routes file:
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
const setRoute = (route) => {
const MyComponent = route.component
return <Route key={route.path} path={route.path} render={routeProps => <MyComponent {...Object.assign({}, routeProps, { routes: rootRoutes[0].routes })} />} />
}
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<div>
{ rootRoutes.map(route => setRoute(route)) }
</div>
</BrowserRouter>
</Provider>
)
}
export default Root