Include collection of routes from another package in Router - javascript

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>

Related

Why my components don't display on my React page?

So I'm learning React and building an app with multiple pages, I made a Routes file which looks like this:
import 'swiper/swiper.min.css';
import React from "react";
import { Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import Catalog from "../pages/Catalog";
import Detail from "../pages/Detail";
const Router = () => {
return (
<Routes>
<Route
path='/:category/search/:keyword'
component={Catalog}
/>
<Route
path='/:category/:id'
component={Detail}
/>
<Route
path='/:category'
component={Catalog}
/>
<Route
path='/'
exact
component={Home}
/>
</Routes>
);
}
And App.js looks like this:
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Header from './components/header/Header';
import Footer from './components/footer/Footer';
import Router from './config/Router';
function App() {
return (
<BrowserRouter>
<Routes>
<Route render={props =>{
<>
<Header {...props}/>
<Router/>
<Footer/>
</>
}}/>
</Routes>
</BrowserRouter>
);
}
export default App;
As you see, I have a browser router and Route which passes props to a component(as I understood) but for some reason the components don't display on the page(original components just have with their name inside of them, but they don't display in App.js).
And my console also says:
No routes matched location "/"
In routes.jsx file. I'm guessing it should lead to main page, but for some reason the route doesn't match and components in App.js don't display.
In Version 6.0.0 there is not any component prop in Route. It has been changed to element. So you need to change your Router to :
const Router = () => {
return (
<Routes>
<Route
path='/:category/search/:keyword'
element={Catalog}
/>
<Route
path='/:category/:id'
element={Detail}
/>
<Route
path='/:category'
element={Catalog}
/>
<Route
path='/'
exact
element={Home}
/>
</Routes>
);
}
As you've said you're using react-router-dom 6.0.2, and it seems that the tutorial you are following is for the older version (5?). There were some breaking changes in version 6.
You need to change your Router component to use element instead of component:
const Router = () => {
return (
<Routes>
<Route path="/:category/search/:keyword" element={<Catalog />} />
<Route path="/:category/:id" element={<Detail />} />
<Route path="/:category" element={<Catalog />} />
<Route path="/" exact element={<Home />} />
</Routes>
);
};
and also your App component seems to be getting in the way with the nested route.
I think it can be simplified to:
function App() {
return (
<BrowserRouter>
<>
<Header />
<Router />
<Footer />
</>
</BrowserRouter>
);
}
You can see a working demo on stackblitz

Error "Error: A <Route> is only ever to be used as the child of <Routes> element"

I am trying to use routing for the first time and followed the exact instructions from Udemy:
File App.js:
import { Route } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";
function App() {
return (
<div>
<Route path = "/welcome">
<Welcome />
</Route>
<Route path = "/game">
<Game />
</Route>
<Route path = "/leaderboard">
<Leaderboard />
</Route>
</div>
);
}
export default App;
File index.js
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from "./App";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
I get the following error:
Error: A Route is only ever to be used as the child of
element, never rendered directly. Please wrap your Route in a Routes.
Where have I gone wrong?
Yes, in react-router-dom version 6 it is a bit different. Please look as the sample below.
React Router tutorial
import { render } from "react-dom";
import {
BrowserRouter,
Routes,
Route
} from "react-router-dom";
import App from "./App";
import Expenses from "./routes/expenses";
import Invoices from "./routes/invoices";
const rootElement = document.getElementById("root");
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="expenses" element={<Expenses />} />
<Route path="invoices" element={<Invoices />} />
</Routes>
</BrowserRouter>,
rootElement
);
There was a fairly decent change between versions 5 and 6 of react-router-dom. It appears that the Udemy course/tutorial is using version 5 where all you needed was a Router to provide a routing context and Route components just needed to be rendered within this context. In version 6, however, the Route components now need to be rendered within a Routes component (which is an upgrade from the v5 Switch component).
Introducing Routes
One of the most exciting changes in v6 is the powerful new <Routes>
element. This is a pretty significant upgrade from v5's <Switch>
element with some important new features including relative routing
and linking, automatic route ranking, and nested routes and layouts.
The error message is pretty clear, wrap your Route components in a Routes component. The routes also don't take children (other than other Route components in the case of nested routes), they render the components as JSX on the new element prop.
function App() {
return (
<div>
<Routes>
<Route path="/welcome" element={<Welcome />} />
<Route path="/game" element={<Game />} />
<Route path="/leaderboard" element={<Leaderboard />} />
</Routes>
</div>
);
}
The problem is your react-router-dom version.
Probably it's 5.1 or higher.
You can try (in terminal):
npm install react-router-dom#5.3.0
And then your code will be OK. Or you better rebuild your code according to new react-router-dom.
import React from 'react'
import {BrowserRouter, Route, Routes } from 'react-router-dom'
import './App.css';
import Navbar from './components/Navbar';
import { Home } from './components/screens/Home';
import { Login } from './components/screens/Login';
import { Profile } from './components/screens/Profile';
import { Signup } from './components/screens/Signup';
function App() {
return (
<BrowserRouter>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
<Route path="/profile" element={<Profile />} />\
</Routes>
</BrowserRouter>
);
}
export default App;
In the latest version of React, 'Switch' is replaced with 'Routes' and 'component' is replaced with 'element'
Enter image description here
Try to wrap your routes by Routes:
import { Route, Routes } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";
function App() {
return (
<div>
<Routes>
<Route path = "/welcome">
<Welcome />
</Route>
<Route path = "/game">
<Game />
</Route>
<Route path = "/leaderboard">
<Leaderboard />
</Route>
</Routes>
</div>
);
}
export default App;
I think there are many problems that can lead to that issue.
react-router-dom version 6 no longer supports the use of components directly. Use an element to specify the component you route.
Route has to be a child of Routes
Use the simple snippet.
import logo from './logo.svg';
import './App.css';
import Navbar from './components/Navbar';
import {BrowserRouter, Routes, Route, Link} from 'react-router-dom';
import Homescreen from './screens/Homescreen';
function App() {
return (
<div className="App">
<Navbar/>
<BrowserRouter>
<Routes>
<Route path='/home' element={<Homescreen/>} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
The problem right here is that you are using React v5. Since React v6, several changes were included in Router.
So now, to make it work, and as your error message says, you need to wrap your Route element inside a Routes element (Routes now is the equivalent, but an improved version of Switch element). Also, you need to add an "element" prop that accepts JSX instead of wrapping inside the Route element.
So, to make it work, you need to import all these elements like this:
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
That being said, your code should look like this:
<Router>
<Routes>
<Route path="/" element={<Welcome/>}>
</Route>
<Route path="/" element={<Game />}>
</Route>
<Route path="/" element={<Leaderboard />}>
</Route>
</Routes>
</Router>
It's probably because you are using version 6 or higher of react-router-dom.
Try:npm i react-router-dom#5.2.0
And it should work.
In the newer version of react-router-dom, we need to nest the Route inside the Routes. Also, component and exact have been removed in newer version.
I was facing same issue and solve it.
Though I am using
react-router-dom#6
So I had to modify app.js and index.js like below
in index.js
import { BrowserRouter } from "react-router-dom";
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
and app.js
import { Routes, Route } from "react-router-dom";
function App() {
return (
<>
<Header />
<main className="py-3">
<Container>
<Routes>
<Route path="/" element={<HomeScreen />} exact/>
</Routes>
</Container>
</main>
<Footer />
</>
);
}
export default App;
according to official documentation
Now, React uses React Router version 6
For React Router version 6, your index.js file is correct:
File index.js:
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from "./App";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
But your App.js file is not correct for React Router version 6, so this is the correct one below:
I changed three parts as shown below:
File App.js
// 1. "Routes" is imported
import { Routes, Route } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";
function App() {
return (
<div> // 2. With "<Routes></Routes>", surround "3 <Route /> tags"
<Routes> // 3. Put an element with a component to each "<Route />"
<Route path = "/welcome" element={<Welcome />} />
<Route path = "/game" element={<Game />} />
<Route path = "/leaderboard" element={<Leaderboard />} />
</Routes>
</div>
);
}
export default App;
Use the element option to set your component instead of nesting it into the route tags. Then wrap all the routes with <Routes></Routes>.
Do not forget to add Routes to your imports
import { Route, Routes } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";
function App() {
return (
<div>
<Routes>
<Route path = "/welcome" element={<Welcome />}/>
<Route path = "/game" element={<Game />}/>
<Route path = "/leaderboard" element={<Leaderboard />}/>
</Routes>
</div>
);
}
export default App;
Use:
<div>
<Header />
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/about" element={<About />} />
</Routes>
I know I'm late but there is another way to do nested routes straight from javascript.
first import
import { useRoutes } from "react-router-dom";
secondly, declare your routes. Here is a quick example
function App() {
return useRoutes([
{
path: "/",
element: <Example/>
},
{
path: "/contact",
element: <Example/>
}]);
}
so now you can have unlimited nested components doing it this way.
in your index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root")); //where App must rendered in real DOM?in root
root.render(<App />); //jsx code is a special syntax that browser not undrestand it!
and in your App.js
import { BrowserRouter, Routes, Route } from "react-router-dom";
import AllMeetupsPage from "./pages/AllMeetups";
import NewMeetupPage from "./pages/NewMeetup";
import FavoritesPage from "./pages/Favorites";
function App() {
return (
<div>
<BrowserRouter>
<Routes>
<Route path="/" element={<AllMeetupsPage />} />
<Route path="/new-meetup" element={<NewMeetupPage />} />
<Route path="/favorites" element={<FavoritesPage />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
There is another way to fix the version issues:
App.js File:
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";
function App() {
return (<div>
<BrowserRouter>
<Routes>
<Route path = "/Welcome" element={< Welcome/>}/>
<Route path = "/Game" element={< Game/>}/>
<Route path = "/LeaderBoard" element={< LeaderBoard/>}/>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
Index.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

Is there a way to check for number of routes in a React component?

Say you have a router setup like this in React, is there a way to check/get the number of routes in this component while writing tests using Jest and Enzyme? In this example, it should return 3 because the PrivateRoute uses Route under the hood. I'm not looking at using an external plugin though
<Router history={history}>
<Switch>
<PrivateRoute exact path='/' component={Component1} />
<Route path='/register' component={Component2} />
<Route path='/login' component={Component3} />
</Switch>
</Router>
Maybe you can try something like this:
import React from 'react';
import { shallow } from 'enzyme';
import { Route } from 'react-router-dom';
import MyCompo from '../MyCompo';
import PrivateRoute fom '../PrivateRoute';
describe('<MyCompo />', () => {
it('should have 3 routes', () => {
const renderedCompo = shallow(<MyCompo />);
expect(renderedCompo.find(Route)).toHaveLength(2);
expect(renderedCompo.find(PrivateRoute)).toHaveLength(1);
})
})

Matched route not changing on routing hash change

I'm using react-router-dom with react-router-redux and history to manage routing for my app. I'm also using hash history for support on legacy browsers. Below are my route components:
<Switch>
<Route exact path={'/'} component={...} />
<Route path={'/a'} component={...} />
<Route path={'/b'} component={...} />
</Switch>
My app lands at the location: http://something.com/index.html#/, and correctly is routed to the first Route component. However, when using dispatch(push('/a')) in a thunk action creator to attempt to programatically switch routes, I'm finding that the proper route is not being matched.
I'm having a difficult time debugging this... any ideas? I'm thinking it perhaps has to do with the fact that my window.location.pathname is /index.html.
Switch receive a location prop, or must be wrapped with Router component. You can find more information at https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md#children-node/
If a location prop is given to the , it will override the location prop on the matching child element.
So try one of these ways:
class Example extends Component {
render() {
return (
<Switch location={this.props.location}>
<Route exact path={'/'} component={...} />
<Route path={'/a'} component={...} />
<Route path={'/b'} component={...} />
</Switch>
);
}
// location is react-router-redux reducer
export default connect(state => ({location: state.location}))(Example);
Or, another way you can do, it's wrap your Switch component with Router component (I pasted code from one of my project):
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import configureStore from './store/configureStore';
const history = createHistory();
const store = configureStore(history);
// We wrap Switch component with ConnectedRouter:
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<Route exact path={'/'} component={...} />
<Route path={'/a'} component={...} />
<Route path={'/b'} component={...} />
</Switch>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
);
More information about Router components you can find here: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Router.md

React - How to include react router?

I'm new to react and I want to use the router to navigate bet pages/components. I couldn't find too much but was following one tutorial online but I don't think I'm doing it right as I'm only getting errors.
When I import router and then have this line: const { Router, Route ...} = ReactRouter; it gives me errors and I'm not sure how to do it right.
I'm not sure where to put the router path. I always see its being put into ReactDOM:render(...). I only render in my index.js file and it just seems wrong to me to put it there as I feel I should only render the App component (for good practice). Am I wrong?
Thanks a lot!
App.js:
import React from 'react';
import ReactDOM from 'react-dom';
import Router from'react-router'
const { Router, Route, IndexRoute, IndexLink, hashHistory, Link } = ReactRouter;
class App extends React.Component {
render () {
return (
<div>
<button><Link to="/page">Page</Link></button>
Router history={ReactRouter.hashHistory}>
<Route path="/" component={App}>
</Route>
<Route path="/page" component={Page}>
</Route>
</Router>
</div>
);
}
}
export default App;
index.js:
ReactDOM.render(<App/>, document.getElementById('app'));
There has been some changes after React router V4, and you should make sure that the code examples you find are right for the version you are using.
I am using v4, and this is what I do:
App.js:
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
....
....
<BrowserRouter>
<Switch>
<Route path="/settings" component={Settings} />
<Route path="/dashboard" component={Dashboard} />
<Route path="/login" component={Login} />
<Route path="/" component={Login} />
</Switch>
</BrowserRouter>

Categories

Resources