Loop through an array to create routes in react router - javascript

I want to hit an API which returns me all the routes of the website that I will need for the react website.
I'm not entirely sure on how to do it or even google an example.
My code looks like this:
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute pageId={5} background="" component={Home}/>
<Route path="/your-journey" background="" pageId={7} component={YourJourney}/>
<Route path="/designed-for-you" background="" pageId={6} component={DesignedForYou}/>
<Route path="/join-us" background="" pageId={1} component={JoinUs}/>
<Route path="/get-in-touch" background="no-hero-image" pageId={4} component={GetInTouch}/>
<Route path="/legal-and-compliance" background="no-hero-image" pageId={8} component={Legal}/>
<Route path="/privacy" background="no-hero-image" pageId={9} component={Privacy}/>
</Route>
</Router>,
document.getElementById('root')
);
Where everything under the Route path="/" needs to come from the API.

Simple, just load the data in some action that loads your routes and map over the result in your ReactDOM.render function. It'll look something like this:
// This just maps the component string names (keys) to actual react components (values)
const COMPONENT_MAP = {
'Privacy': Privacy, // quotes are not necessary, just illustrating the difference a bit more
// ... other mappings
}
// Some asynch action that loads the routes from your API
getRoutes().then((routes) => {
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute pageId={5} background="" component={Home}/>
{routes.map((r) => {
return <Route path={r.path} background={r.bg} pageId={r.pageId} component={COMPONENT_MAP[r.component]}/>
}}
</Route>
</Router>,
document.getElementById('root')
);
});

Related

React router v6, group routes by feature

I am using React router v6.
THE PROBLEM:
my App.tsx is very big as it includes routes for all of the application:
Ex.
...
<Route path="products/" element={<ProductsList />} />
<Route path="products/:slug" element={<ProductView />} />
... about 300 lines
I would like to group these routes by feature so that I end up having something like this:
...
<Route path="admin" element={<AdminRoutes />} />
<Route path="products" element={<ProductsRoute />} />
...
it would look cleaner and easier to read.
So far I have created something like this for the Admin section:
export const AdminRoutes = (): any => {
return (
<Routes>
<Route path="admin" element={<Admin />}>
</Routes>
)}
and I have imported it like this inside App.tsx:
...
<Route element={<AdminRoutes />} path="admin" />
...
I am expecting to see the <Admin /> component (defined in AdminRoutes), although I don't get any errors the screen is blank.
export const AdminRoutes = (): any => {
return (
<Routes>
<Route path="admin" element={<Admin />}>
</Routes>
)}
Since you're using relative paths, the actual url that this will match is /admin/admin, one comes from the top level route in App, and another from here. Assuming you wanted this to only match "/admin", you can instead do:
<Route path="*" element={<Admin />}/> // Matches /admin
<Route path="dashboard" element={<Dashboard/>}/> // Matches /admin/dashboard
Or you could use an absolute path:
<Route path="/admin" element={<Admin />}/>
<Route path="/admin/dashboard" element={<Dashboard/>}

How to add ```Context``` with routes set up?

I want to add the searchContext context in my App.js so that my Navbar an Results components have access to the variables in it. How would I do this with my routes set up? I tried to just add with with them but that didn't work. This is the code.
<Router>
// Where does <searchContext.Provider> go?
<Navbar></Navbar>
<Routes>
<Route exact path='/' element={<Home />} />
<Route path='/results'>
<Route path='/results/:value' element={<Results />} />
{/*<Route path='/results/:value/:slot' element={<Slot />}*/}
</Route>
// Another route that has nothing to do with searchContext
</Routes>
</Router>
Edit: Added "// Another route that has nothing to do with searchContext"
if i were you i would just wrap the all thing with the provider as long as its not to heavy.
Hellow, this goes with general understanding of context Api,
now the structure of your folders is your choice but i'll be very brief here
//on your context file searchContext.js
import {useState,createContext} from 'react';
export const SearchContext = createContext(null);
export const ContextProvider = props => {
const [search,setSearch] = useState(null);
return <SearchContext.Provider value = {{search,setSearch}}>
{children>
</SearchContext.Provider>
}
//now on your route.js
import { ContextProvider} from '../your/path/file';
....
<Router>
// Where does <searchContext.Provider> go?
<ContextProvider>
<Navbar></Navbar>
<Routes>
<Route exact path='/' element={<Home />} />
<Route path='/results'>
<Route path='/results/:value' element={<Results />} />
{/*<Route path='/results/:value/:slot' element={<Slot />}*/}
</Route>
// Another route that has nothing to do with searchContext
</Routes>
</ContextProvider>
</Router>

React Router Redux always show first route

React router redux always show the first route. No matter which url i entered, it will render the first route.
Index.js file
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
App.js file
export default function App() {
return (
<Switch>
<Route to="/" component={Dashboard} key={1} />;
<Route to="/icons" component={Icons} key={2} />;
</Switch>
);
}
just add exact={true} to Route, default exact is set to false, just take a look here.
<Route exact path="/" component={Dashboard} key={1} />;
When you make use Switch, you need to add the Routes, whose paths are prefixes to other Routes at the end since Switch matches and renders the first route that matched.
export default function App() {
return (
<Switch>
<Route to="/icons" component={Icons} key={2} />
<Route to="/" component={Dashboard} key={1} />
</Switch>
);
}
And you don't need ; at the end of JSX statements
This is because of the bad route configuration in your example. You need to add exact prop to the first route.
Example:
<Switch>
<Route exact to="/" component={Dashboard} />;
<Route to="/icons" component={Icons} />;
</Switch>
If you don't add exact to the "/" route, then it will always be matched, because every other route also has the / as its part.
My suggestion is to define all routes as exact by default, and only set non-exact routes when required such as:
<Switch>
<Route exact to="/" component={Dashboard} key={1} />;
<Route exact to="/icons" component={Icons} key={2} />;
<Route to="/icons/:iconId" component={IconDetails} key={3} />;
</Switch>

Creating react-router Routes in a function

Is it possible to create the <Route> elements through a function? I would like to display different routes/pages and the application is not aware how many they are. The pages are in the state stored in an array of page objects. Each page object have an id, title, path and body elements (so far). I think that my Router should look like this:
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="page1" component={CommonPage}/>
<Route path="page2" component={CommonPage} />
<Route path="page3" component={CommonPage} />
<Route path="page4" component={CommonPage}/>
<Route path="page5" component={CommonPage} />
<Route path="page6" component={CommonPage} />
</Route>
<Route path="*" component={NoMatch}/>
</Route>
</Router>
), document.body)
And I would like to define it with a function like:
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
{
mapPagesToRoutes(pages)
}
<Route path="*" component={NoMatch}/>
</Route>
</Router>
), document.body)
mapPagesToRoutes(pages) {
return pages.map( page => () {
if(page.type === 'CommonPage' {
return <Route path={page.path} component={CommonPage}
}
}
}
Q1: How is this done?
Q2: How do I identify each with something other than the path. Can I use some sort of key=page.id in <Route>?
If you want to pass properties to your child route components, you can do something like this:
<Route path={page.path} component={() => <CommonPage someProp={page.someProp}/>}/>
If you want to dynamically create React Routes then you can use a route with a parameter, like so. Here's your Router:
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="/page:pageNumber" pages={this.state.pages} component={CommonPage} />
<Route path="*" component={NoMatch}/>
</Route>
</Router>
), document.body)
Then in your CommonPage component have something like this:
render() {
return(<div>This is {this.props.params.pageNumber}</div>);
}
That should give you the ability to navigate to any /pageX route. Then in CommonPage component you can compare the pageNumber parameter and the pages property (available via this.props.route.pages) to see if the current page number is valid

How do I modify the route parameters sent as props with React Router?

I have a React Router with routes like this:
<Router history={history}>
<Route path='/' component={App} />
<Route path='/:fileType/:fileId' component={App} />
</Router>
This puts props into my App like so:
{
fileType: 'whatever',
fileId: 'ABC5734'
}
However, I have designed my component so that it expects this format:
{
file: {
type: 'whatever',
id: 'ABC5734'
}
}
I would therefore like to transform the path props before they are sent to the component. Something like this:
<Router history={history}>
<Route path='/' component={App} />
<Route
path='/:fileType/:fileId'
component={(props) => <App file={{type: props.fileType, id: props.fileId}} />} />
</Router>
Is this possible?
You should use a Higher-Order Component.
You could use the mapProps Higher-order Component from recompose :
import mapProps from 'recompose/mapProps'
<Router history={history}>
<Route path='/' component={App} />
<Route
path='/:fileType/:fileId'
component={mapProps(({ params: { fileType, fileId } }) => ({
fileType,
fileId
}))(App)}/>
</Router>
React-router sends route params under this.props.params. So, correct your your code.
<Router history={history}>
<Route path='/' component={App} />
<Route
path='/:fileType/:fileId'
component={(props) => <App file={{type: props.params.fileType, id: props.params.fileId}} />} />
</Router>

Categories