How to realize <Route children={({match})} => ... /> in a react-router-dom v6? - javascript

I have a modal which depended on a url.
my modal in jsx
<Route
path={`/preview/${params.id}`}
children={({match}) => {
return (
<ModalWindow
modalVisible={Boolean(match)}
onCloseWindow={onCloseWindow}
modalContent={modal}
/>
)
}}
/>
I am getting an error A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.
But I already wrapped all my app in a tag.
export default function AppRouter() {
return (
<Routes>
<Route
path={"/home"}
element={<HomeApp />}
/>
<Route
path={"/preview/:id"}
exact
element={<HomeApp />}
/>
<Route
path={"/fullInfo/:id"}
exact
element={<HomeApp />}
/>
<Route
path={"*"}
render={<Navigate to="/home"/>}
/>
</Routes>
)
}
P.s I am new in react-router-dom of the new version.

You have to wrap the rendered element in the element prop of your Route.So instead of :
<Route
path={"*"}
render={<Navigate to="/home"/>}
/>
Do this:
<Route
path={"*"}
element={<Navigate to="/home"/>}
/>
It is the same for the modal Route, you should wrap the component in the element prop of the Route

Related

Changing from v5 to v6, how to make it work with relative paths?

My application has scenarios where we need several routes to "pass" through a component to only then render the specifics, not only that but also situations where something is shown for the "parent" route and then split for the children...
It is imperative to note that we don't have a single "route config" file, and instead our routes are where we need them.
This was possible with v5, but I am very confused about how to get this accomplished with the new version.
So, currently we have stuff such as:
App.js
function App = () => {
return (
<Switch>
<Route exact path={['/', '/2', '/more-info']} component={Login} />
<Route path="/(main|settings|notifications)" component={AuthenticatedUser} />
<Redirect from="*" to="/404" />
</Switch>
);
}
AuthenticatedUser.js
function AuthenticatedUser= () => {
{... lots of common code}
return (
<div>
{...common html}
<Switch>
<Route exact path="/main" component={Main} />
<Route path="/settings" component={Settings} />
<Route path="/notifications" component={Notifications} />
</Switch>
</div>
);
}
Settings.js
function Settings= () => {
{... lots of common code}
return (
<div>
{...common html}
<Switch>
<Route exact path="/settings/basic" component={Basic} />
<Route exact path="/settings/notifications" component={Notifications} />
</Switch>
</div>
);
}
Now, with the relative from the parent, I am not able to get the same structure, I am also confused about how to get the routes split into separate files not even talking about the regex situation that I am guessing the solution is to duplicate the lines as many times as I have items in that regex...
You have basically 2 options when it comes to declaring the routes and sharing common UI:
Use layout routes and nested Route components.
Render routed components that render descendent routes in another Routes component wrapping descendent Route components.
Using layout and nested routes
Convert AuthenticatedUser into a layout route. Layout routes render an Outlet for nested routes to render their matched element into.
import { Outlet } from 'react-router-dom';
function AuthenticatedUser = () => {
{... lots of common code}
return (
<div>
{...common html}
<Outlet />
</div>
);
};
Convert Settings also into a layout route component.
import { Outlet } from 'react-router-dom';
function Settings = () => {
{... lots of common code}
return (
<div>
{...common html}
<Outlet />
</div>
);
};
App
import { Routes, Route, Navigate } from 'react-router-dom';
function App = () => {
return (
<Routes>
<Route path="/" element={<Login />} />
<Route path="/2" element={<Login />} />
<Route path="/more-info" element={<Login />} />
<Route element={<AuthenticatedUser />}>
<Route path="/main" element={<Main />} />
<Route path="/settings" element={<Settings />}>
<Route
path="basic" // "/settings/basic"
element={<Basic />}
/>
<Route
path="notifications" // "/settings/notifications"
element={<Notifications />}
/>
</Route>
<Route path="/notifications" element={<Notifications />} />
</Route>
<Route path="*" element={<Navigate to="/404" replace />} />
</Routes>
);
};
Using descendent routes
Here the parent routes need to render their route path with a trailing "*" wildcard matcher so descendent routes can also be matched. Descendent Routes components build their route paths relative to their parent Route path. I'd still suggest using AuthenticatedUser as a layout route for ease, otherwise you'll have a lot of code duplication since you'd need to wrap each route individually.
App
import { Routes, Route, Navigate } from 'react-router-dom';
function App = () => {
return (
<Routes>
<Route path="/" element={<Login />} />
<Route path="/2" element={<Login />} />
<Route path="/more-info" element={<Login />} />
<Route element={<AuthenticatedUser />}>
<Route path="/main" element={<Main />} />
<Route path="/settings/*" element={<Settings />} />
<Route path="/notifications" element={<Notifications />} />
</Route>
<Route path="*" element={<Navigate to="/404" replace />} />
</Routes>
);
};
Settings
import { Routes } from 'react-router-dom';
function Settings = () => {
{... lots of common code}
return (
<div>
{...common html}
<Routes>
<Route
path="/basic" // "/settings/basic"
element={<Basic />}
/>
<Route
path="/notifications" // "/settings/notifications"
element={<Notifications />}
/>
</Routes>
</div>
);
};

useParams() is empty in react-router

I'm trying to render the params in the url as an h2 in in the website. But even when I try console.log useParams is empty.
Here's my Router.js file
const Webpages = () => {
return (
<Router>
<Routes>
<Route exact path="/" element={Home()} />
<Route exact path="/comics" element={Comics()} />
<Route path='/comics/:comicId' element={Comic()} /> <--------------
<Route exact path="/portfolio" element={Portfolio()} />
<Route exact path="/blog" element={Blog()} />
<Route exact path="/contact" element={Contact()} />
<Route exact path="/store" element={Store()} />
<Route path="*" element={NotFound()} />
</Routes>
</Router>
);
};
export default Webpages;
Here's my comic component
import React from 'react';
import NavBar from '../../components/NavBar';
import { useParams } from 'react-router-dom';
function Comic() {
let { comicId } = useParams();
console.log(comicId);
return (
<div>
<NavBar />
<p>{comicId}</p>
</div>
)
}
export default Comic
The page element when I go to a random comic works fine, like localhost:3000/comics/465456 but the tag is empty and the console log is undefined, it's also undefined if I just try to console log useParams()
You are directly invoking the React function. Directly invoking React functions is not how React works.
The Route component's element prop expects a React.ReactNode, a.k.a. JSX. Pass the components as JSX. The JSX is transpiled down to vanilla Javascript and the React framework handles calling your function within the confines of the React component lifecycle.
Example:
const Webpages = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/comics" element={<Comics />} />
<Route path='/comics/:comicId' element={<Comic />} />
<Route path="/portfolio" element={<Portfolio />} />
<Route path="/blog" element={<Blog />} />
<Route path="/contact" element={<Contact />} />
<Route path="/store" element={<Store />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
};

React Router v6 error: All component children of <Routes> must be a <Route> or <React.Fragment>

The following React routes code probably works in React Router v5, but gives the following error in React Router v6
Error: [Player] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
Is it possible to update the Routes/Route code so that it works in React Router v6?
function App() {
// Some stuff here...
const { players, offlinePlayers } = usePlayers();
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/players">
{players.map((player) => {
return (
<Route exact key={player.name} path={`/players/${player.name}`}>
<Player player={player} />
</Route>
);
})}
</Route>
</Routes>
</BrowserRouter>
</ThemeProvider>
)
}
The Player component should be rendered by a Route component on the element prop, not as a child of the Route.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/players">
{players.map((player) => (
<Route
key={player.name}
path={`/players/${player.name}`}
element={<Player player={player} />}
/>
))}
</Route>
</Routes>
</BrowserRouter>
You should map Routes in their parent route.
Like:
<Route path="/players">
{players.map((player) => (
<Route exact key={player.name} path={`/players/${player.name}`}>
<Player player={player} />
</Route>
);
)}
</Route>
But if you want to render dynamic player then dont use the above code for that purpose because its not best approach if you are using dynamic player.name. In your code you are creating each route for every player.
So, use the following code:
<Route path="/players">
<Route exact path={":playerName"} element={<Player/>} />
</Route>
And in Player component, extract playerName from params like:
let { playerName } = useParams();

How to prioritize the route in React?

I'm making React app, and I have some Routers.
const App = () => {
return (
<div>
<Header/>
<div>
<Route path="/LogIn" render={() => <LogIn />} />
<Route path="/Shop" render={() => <GoodsContainer />} />
<Route path="/Delivery" render ={() => <Delivery />} />
</div>
<Footer />
</div>
)
}
When I open my page for the first time, there're only Headercomponent and Footer component, and It's logically because the url has not contain pathes which I have set to the Routes.
So, my question is how to show always , for example the Route <Route path="/Shop" render={() => <GoodsContainer />} /> when the user open the site.
Redirect isn't my solution.
path prop could either be a string or an array of strings. You can define multiple paths for GoodsContainer component using an array of strings as a value for path prop
<Route path={["/", "/Shop"]} render={() => <GoodsContainer />} />
and don't forget to either use exact prop on Route component or wrap all Route component with Switch component otherwise / path will match all other routes.
You can check #Yousef answer for this , Also if you arent passing any props, then do this instead. Don't forget to give the exact
<Route path={["/", "/Shop"]} exact render={GoodsContainer} />
But if you are passing then do this
<Route path={["/", "/Shop"]} exact render={(props)=> <GoodsContainer {...props} />} />

Nested React Router : hide parent component on showing nested child component

Being a beginner in reactJS, I want to know how to hide parent component when i route to child component URL
Assume a scenario:
User is at "/house" as this:
<Route path="/house" component={House} />
when user clicks a house grid he navigates to "/house/flat/:flatID". Inside House component
<Route
path={`${this.props.match.url}/flat/:flatId`}
render={() => <div>Each Flat details</div>}
/>
then both child and parent components are visible like this:
So what i want is to show only flat component when user navigates to "/house/flat:FlatId". Please suggest me anything that helps ! Any links to article so that i can learn and achieve such functionality.
Code:
App.js
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/account" component={Account} />
<Route path="/gharjagga" component={GharJagga} />
</Switch>
House.js
onGharGridClick= id => {
<Redirect to={`${this.props.match.url}/flat/${id}`} />;
};
return (
<Route
path={`${this.props.match.url}/flat/:fkatId`}
render={() => <div>Ghar Each</div>}
/>
);
You can achieve it different ways
define routes in the parent component, I think this is the best option.
<Switch>
<Route path="/account" component={Account} />
<Route path="/house/flat/:flatId" component={FlatComponent}/>
<Route path="/house" component={House} />
<Route path="/" component={Home} />
</Switch>
Note: instead of using exact, order your routes based on priority, that will make the route to redirect to next matching route if any typo in the route entered
You can make House as separate route component, and nest the routes inside that component
// Routes
<Switch>
<Route path="/account" component={Account} />
<Route path="/house" component={House} />
<Route path="/" component={Home} />
</Switch>
// House component
class House extends React. Components {
render() {
return (
<Switch>
<Route path="/house/flat/:flatId" render={() => <div>Each Flat details</div>} />}/>
<Route path="/house" component={HouseGridComponent} />
</Switch>
)
}
}
you can check whether the route has flatId and hide the elements, in your House component you can check this.props.match.params.flatId, if the flatId is set you can hide that House Component.
// House Component
class House extends React. Components {
render() {
return (
<div>
{
!this.props.match.params.flatId?
<h1>House Component</h1>:
null
}
<Route path={`${this.props.match.url}/flat/:flatId`} render={() => <div>Each Flat details</div>} />
</div>
)
}
}
The solution is to raise "/house/flat/:flatId" to the same level as "/house".
<Switch>
<Route exact path="/" component={Home} />
<Route path="/account" component={Account} />
<Route path="/house/flat/:flatId" render={() => <div>Each Flat details</div>}/>
<Route path="/house" component={House} />
</Switch>

Categories

Resources