I started to build next website using reactJS and nodejs. The website is required to have minimum 2 languages and it should be possible to share link to website in desired language. I concluded quickly that what i need is to have following url structure
domain.com/en-GB
domain.com/en-GB/gallery
domain.com/en-GB/contact
front end Router:
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
...
<Router>
<React.Fragment>
<Header />
<Suspense fallback={<Loading />}>
<Switch>
<Route exact path="/">
<Landing />
</Route>
<Route path="/gallery">
<Gallery />
</Route>
<Route path="/about-us">
<AboutUs />
</Route>
<Route path="/contact">
<Contact />
</Route>
<Route>
<PageNotFound />
</Route>
</Switch>
</Suspense>
<Footer />
</React.Fragment>
</Router>
...
front end menu:
import { NavLink } from "react-router-dom";
...
<NavLink to="/about-us">
{Lang.resolve("HEADER_MENU_ABOUT_US")}
</NavLink>
...
pieces of webpack config:
...
const extractTextPlugin = new ExtractTextPlugin({
filename: "css/style.[hash:8].css",
allChunks: true
});
module.exports = {
...
output: {
filename: "js/main.[hash:8].js"
}
...
},
...
server routing:
...
const router = express.Router();
let landing = (req, res, next) => {
res.sendFile(path.join(__dirname, "../../index.html")); // html build by webpack
};
router.get("/", landing);
router.get("/:view", landing);
...
I was wondering if there is something already baked into reactJS to allow this or i need to manually append language part of url like: <Route path={_LANG_ + "/gallery"}>.
Probably some adjustments are also needed to the server routing and webpack paths otherwise it will be a problem while loading css, js files domain.com/en-GB/css/style.css.
Maybe someone already fought this idea and could have some tips.
I am not sure how you gonna handle the translations on each component, but if you have different components (according to the language), then an option would be having different "basenames" on your Router:
<BrowserRouter basename="/calendar">
<Link to="/today"/> // renders <a href="/calendar/today">
<Link to="/tomorrow"/> // renders <a href="/calendar/tomorrow">
...
</BrowserRouter>
From the documentation:
https://reacttraining.com/react-router/web/api/BrowserRouter/basename-string
Similar question:
Multiple BrowserRouter Shows Multiple Components
Related
I am having trouble writing code to render a login page with no navbar and sidebar. I have come across some pages that ask similar questions but none seem to pertain to my current situation.
How to hide navbar in login page in react router
the example given is great but I believe the way of accomplishing that same task has changed with react-router-dom v6 leading me to read about this change in https://dev.to/iamandrewluca/private-route-in-react-router-v6-lg5
It seems I am not understanding a certain aspect about routing with React Router. In the code below I have two Routes. One of the routes(Login) I would like to have render without the NavBar and SideBar component.
const App = () => {
return (
<>
<Routes>
<Route path="/login" element={<LoginPage />} />
</Routes>
<NavBar />
<SideBar />
<main className={styles["main--container"]}>
<div className={styles["main--content"]}>
<Routes>
<Route path="/" element={<Dashboard />} />
</Routes>
</div>
</main>
</>
);
};
An alternative, that I also tried, would be to move the NavBar and SideBar tags into the Dashboard component, but then I would essentially have to do the same copy and paste for any new components. This method felt wrong and inefficient , but if this is the correct way of doing it I will do the needful
Edit: I think it's important to include what it currently does is load the Login page with the NavBar and SideBar included. Navigating to the dashboard component has the NavBar and SideBar but this is intended.
What I would like is for the Login page not to have the NavBar and SideBar
If I understand your question, you are wanting to render the nav and sidebar on the non-login route. For this you can create a layout component that renders them and an outlet for the nested routes.
Using nested routes
import { Outlet } from 'react-router-dom';
const AppLayout = () => (
<>
<NavBar />
<SideBar />
<main className={styles["main--container"]}>
<div className={styles["main--content"]}>
<Outlet /> // <-- nested routes rendered here
</div>
</main>
</>
);
const App = () => {
return (
<>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route element={<AppLayout />} >
<Route path="/" element={<Dashboard />} /> // <-- nested routes
</Route>
</Routes>
</>
);
};
Using a routes configuration and useRoutes hook
const routesConfig = [
{
path: "/login",
element: <LoginPage />,
},
{
element: <AppLayout />,
children: [
{
path: "/",
element: <Dashboard />,
},
],
},
];
...
import { useRoutes } from 'react-router-dom';
const App = () => {
const routes = useRoutes(routesConfig);
return routes;
};
Using a routes configuration and data routers (introduced in v6.4.0)
const routesConfig = [
{
path: "/login",
element: <LoginPage />,
},
{
element: <AppLayout />,
children: [
{
path: "/",
element: <Dashboard />,
},
],
},
];
...
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const router = createBrowserRouter(routesConfig);
const App = () => {
return <RouterProvider router={router} />;
};
The easiest way for you to hide the navbar would be to go to the login page component and call useLocation(). Then you woulf do something like this after declaring the use location. And assigning it to a variable location
{ location.pathname === "/login" ? null : (
Render the whole navbar component);
Not sute if you can be able to read as I type from my phone
I am having trouble writing code to render a login page with no navbar and sidebar. I have come across some pages that ask similar questions but none seem to pertain to my current situation.
How to hide navbar in login page in react router
the example given is great but I believe the way of accomplishing that same task has changed with react-router-dom v6 leading me to read about this change in https://dev.to/iamandrewluca/private-route-in-react-router-v6-lg5
It seems I am not understanding a certain aspect about routing with React Router. In the code below I have two Routes. One of the routes(Login) I would like to have render without the NavBar and SideBar component.
const App = () => {
return (
<>
<Routes>
<Route path="/login" element={<LoginPage />} />
</Routes>
<NavBar />
<SideBar />
<main className={styles["main--container"]}>
<div className={styles["main--content"]}>
<Routes>
<Route path="/" element={<Dashboard />} />
</Routes>
</div>
</main>
</>
);
};
An alternative, that I also tried, would be to move the NavBar and SideBar tags into the Dashboard component, but then I would essentially have to do the same copy and paste for any new components. This method felt wrong and inefficient , but if this is the correct way of doing it I will do the needful
Edit: I think it's important to include what it currently does is load the Login page with the NavBar and SideBar included. Navigating to the dashboard component has the NavBar and SideBar but this is intended.
What I would like is for the Login page not to have the NavBar and SideBar
If I understand your question, you are wanting to render the nav and sidebar on the non-login route. For this you can create a layout component that renders them and an outlet for the nested routes.
Using nested routes
import { Outlet } from 'react-router-dom';
const AppLayout = () => (
<>
<NavBar />
<SideBar />
<main className={styles["main--container"]}>
<div className={styles["main--content"]}>
<Outlet /> // <-- nested routes rendered here
</div>
</main>
</>
);
const App = () => {
return (
<>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route element={<AppLayout />} >
<Route path="/" element={<Dashboard />} /> // <-- nested routes
</Route>
</Routes>
</>
);
};
Using a routes configuration and useRoutes hook
const routesConfig = [
{
path: "/login",
element: <LoginPage />,
},
{
element: <AppLayout />,
children: [
{
path: "/",
element: <Dashboard />,
},
],
},
];
...
import { useRoutes } from 'react-router-dom';
const App = () => {
const routes = useRoutes(routesConfig);
return routes;
};
Using a routes configuration and data routers (introduced in v6.4.0)
const routesConfig = [
{
path: "/login",
element: <LoginPage />,
},
{
element: <AppLayout />,
children: [
{
path: "/",
element: <Dashboard />,
},
],
},
];
...
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const router = createBrowserRouter(routesConfig);
const App = () => {
return <RouterProvider router={router} />;
};
The easiest way for you to hide the navbar would be to go to the login page component and call useLocation(). Then you woulf do something like this after declaring the use location. And assigning it to a variable location
{ location.pathname === "/login" ? null : (
Render the whole navbar component);
Not sute if you can be able to read as I type from my phone
ISSUE
any route written manually in URL or triggered by button renders only index page (route name is changing, content of the page is always content of index)
App.js (handling all the main functions from the app)
// Exporting my router and passing all states/functions from App
<div className="App">
<Routes
........
........
........
onRemove={onRemove}
setCartOpen={setCartOpen}
cartOpen={cartOpen}
.......
.......
/>
</div>
Router (example of how my routes work)
export default function Routes({....., ....., onRemove,setCartOpen,cartOpen,..... })
<Router>
<Header
countCartItems={cartItems.length}
cartItems={cartItems}
onAdd={onAdd}
onRemove={onRemove}
setCartOpen={setCartOpen}
/>
<Suspense fallback={<Spinner />}>
<Switch>
<Route
path={baseRouteUrl + '/'}
component={(props) => (
<IndexPage
{...props}
.......
.......
onRemove={onRemove}
setCartOpen={setCartOpen}
cartOpen={cartOpen}
.......
.......
/>
)}>
</Route>
<Route
path={baseRouteUrl + '/about'}
component={About}
></Route>
<Route
path={baseRouteUrl + ''}
component={NotFound}
></Route>
.......
And Index.js
ReactDOM.render(
<React.StrictMode>
<QueryClientProvider client={client}>
<ChakraProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</ChakraProvider>
</QueryClientProvider>
</React.StrictMode>,
document.getElementById('root')
);
NOTE
Before trying to pass props in the routes the router was working just fine
The reason I decided to structure my app this way is because I need my Header to be exported on all pages with all the functionalities.
Is there anything I should do in a different way? I know it might be a little messy, so I'll be grateful to learn how to handle it cleaner and most important, how to make it work so all routes will render the correct pages.
Just like in most languages, a switch will match and runs the first thing it runs into.
In this case, it's matching your default path and rendering that one first.
One of the way to deal with this is to set exact to true
<Route exact path={...}... />
react router does partial matching so '/' is partial from '/about' when using exact it will disable the partial matching and will get the correct path
<Route
exact
path={baseRouteUrl + '/'}
component={(props) => (
<IndexPage
{...props}
.......
.......
onRemove={onRemove}
setCartOpen={setCartOpen}
cartOpen={cartOpen}
.......
.......
/>
)}>
</Route>
Quick question here, I have the below code, and I want to be able to import a whole lot of routes from my package. The routes that are imported should be controlled by the package I'm building. If I add a new page in the package (say, ForgotPassword), then I won't want to come here and manually add an entry for ForgotPassword... It should just start working when I update to the latest version of the package.
Also, what will this route collection look like in my package project?
Any ideas welcome :D
...
import { RouteCollectionFromPackage } from "#my/package";
...
<Router basename="/">
<Suspense fallback={<div>Loading...</div>}>
<Switch>
{ /* I WANT TO IMPORT A COLLECTION OF ROUTES FROM MY PACKAGE */}
<RouteCollectionFromPackage />
{ /* THESE ARE IN MY APP */}
<Route exact path="/" component={home} />
<Route exact path="/search" component={search} />
</Switch>
</Suspense>
</Router>
Thanks!!
EDIT:
This is what I have tried, after following some of the suggestions below:
In my module:
const Routes = [
<Route exact path="/Login" component={Login} />,
<Route exact path="/ForgotPassword" component={Login} />,
<Route exact path="/MyProfile" component={Login} />
];
export { Routes };
In my consuming app:
import { Suspense, lazy } from "react";
import { HashRouter as Router, Switch, Route } from "react-router-dom";
import { Routes as PortalFrameworkRoutes, Login} from "#sal/portal";
const home = lazy(() => import("./pages/home/Home"));
const search = lazy(() => import("./pages/search/Search"));
function routes() {
return (
<Router basename="/">
<Suspense fallback={<div>Loading...</div>}>
<Switch>
{PortalFrameworkRoutes.map((route: Route) => route)}
<Route exact path="/" component={home} />
<Route exact path="/search" component={search} />
</Switch>
</Suspense>
</Router>
);
}
export default routes;
I get the error:
Error: Invariant failed: You should not use <Route> outside a <Router>
OR when I use {...PortalFrameworkRoutes} I get:
Spread children are not supported in React
EDIT #2:
This may actually be a crucial bit of information I omitted. In my module, the route is exported, and imported (and again exported) in an index.tsx like this:
export { Routes } from "./routes";
export { Login } from "./pages/login/Login";
I'm not sure if this is 100% correct, but it feels correct since I just want to do an import from the top level of my module, and have everything available there. i.e. import { Routes as PortalFrameworkRoutes, Login } from "#sal/portal";
In your package, export the routes like so:
const yourRoutes = [
<Route ... />,
<Route ... />,
];
export { yourRoutes };
Import it in your consuming application:
import { yourRoutes } from '#your/package';
Then use the array spread operator to include them along the other routes:
<Switch>
{...yourRoutes}
<Route path="/some/application/route" component=... />
</Switch>
In your "#my/package" module, you should export an Array of objects where each object has a path and component property which you can then dynamically render
Then in your current file, you can use the map method to render them
//"#my/package" module
//make sure to import the necessary components you will be adding
import component1 from "//..."
import component2 from "//..."
.
import componentN from "//..."
export myRoutes = [
{path: "/pathToComponent1", component: component1}
{path: "/pathToComponent2", component: component2}
.
{path: "/pathToComponentN", component: componentN}
]
// in your current module
import {myRoutes} from "#my/package"
<Router>
<switch>
...
// where you need to render routes from your module
{myRoutes.map(route => <Route path={route.path} component={route.component}/>}
...
</switch>
</Router>
I have installed Symfony + React application like AdminLTE. Everything works perfect, but I have run into a wall when I need to create dynamic route with id as a parameter. I have page with templates, each template has id, but I am getting route not found error. I guess I am missing some step. My code structure is like this:
// Default Controller
class DefaultController extends AbstractController
{
/**
* #Route("/admin/{reactRouting}", name="home", defaults={"reactRouting": null})
*/
public function index()
{
return $this->render('default/index.html.twig');
}
}
For sidebar, there is route.js file with routes that are generated like this:
const dashboardRoutes = [
{
path: "/documents",
name: "Documents",
rtlName: "لوحة القيادة",
icon: Dashboard,
component: Documents,
layout: "/admin"
},
{
path: "/importer",
name: "Importer",
rtlName: "لوحة القيادة",
icon: Dashboard,
component: Importer,
layout: "/admin"
}
}
And in my app.js I have generated main routes like this:
ReactDOM.render(
<Router history={hist}>
<Switch>
<Route path="/" component={Admin} />
<Route path="/admin" component={Admin} />
</Switch>
</Router>,
document.getElementById("root")
);
When you click on the Documents link, you have a list of templates with specific id: (template 1, template 2...) like this:
<CardFooter chart>
<Link className={"nav-link"} to={`/admin/template/${data.template.id}`}> Template {data.template.id} </Link>
<div className={classes.stats}>
<AccessTime /> updated 4 minutes ago
</div>
</CardFooter>
I need that link to open a new component TemplateLayout to display the details about specific template. So I tried to add in app.js main routes code like this:
<Route exact path="/admin/template/:id" component={TemplateLayout} />
But it is rendering sidebar with blank page without errors.(http://localhost/admin/template/1). And this is the content of TemplateLayout file:
export default function TemplateLayout() {
const classes = useStyles();
console.log('tesla');
return (
<div>Test</div>
);
}
Also, when I refresh http://localhost/admin/template/1 I get symfony route not found error. I cannot find what am I missing, any clue is welcome. Thank you.
First you need change Symfony route to
/**
* #Route("/admin/template/{reactRouting}", name="home", defaults={"reactRouting": null})
*/
then in App.js change your Route to exact
ReactDOM.render(
<Router history={hist}>
<Switch>
<Route path="/" exact component={Admin} />
<Route path="/admin" exact component={Admin} />
</Switch>
</Router>,
document.getElementById("root")
);