How to pass props between React Routes 4 - javascript

I can't find the way how to send object from one React Route Component to another.
For example I have container router like this:
const App = () => (
<Router>
<div>
<Route exact path="/" component={Home} />
<Route path="/sections/:id"
component={Section} />
</div>
</Router>
)
with Home component like this:
const Home = () => {
const Sections = tilesData.map( (section, index) => (
<div>
<img src={section.img} height="200" /><br/>
<Link to={`/sections/'${section.id}`} >Details for {section.author}</Link>
<hr/>
</div>
))
return(
<div>
{Sections}
</div>
)
}
and I don't understand =\ how to pass selected object when next route clicked with <Link>. Here is example component:
const Section = (props) => {
return(
<div>
Section {props.title}
<img src={props.img} />
</div>
)
}
Here is code example: https://codesandbox.io/s/Mv037AE3

In react-router v4, we usually do the following to pass in a ProductPage component:
<Route path='/' component={ProductPage} />
When you want to use props, you can prepare a function that returns your component with the desired props. Here I'm preparing to pass in a toggleSidebarOn prop:
const MyProductPage = (props) => {
return (
<ProductPage
toggleSidebarOn={this.toggleSidebarOn.bind(this)}
{...props}
/>
);
}
Then you use the render prop of the <Route /> instead of its component prop. Do not use both and do not use the component prop as this leads to undesired remounting and might break your app (e.g. for me CSS transitions for the sidebar animation stopped working properly).
Using the render prop for the MyProductPage we can then pass in our toggleSidebarOn prop to its target route:
<Router>
<div>
<Switch>
<Route exact path="/products" render={MyProductPage} />
<Route exact path="/perspectives" component={PerspectivePage}/>
<Route component={NotFound}/>
</Switch>
</div>
</Router>
Hope this helps!

Related

section is not a <Route> component. All component children of <Routes> must be a <Route>

Register and login components need to be added to the container class. I followed a react course on Udemy. They are using an older version of react-router-dom. For this i used v6 react router dom and made changes, but this one I don't know what to do. This code is new to me, please assist me
function App() {
return (
<Router>
<Fragment>
<Navbar />
<Routes>
<Route exact path='/' element={<Landing />} />
<section className='container'>
<Route exact path='/register' element={Register} />
<Route exact path='/login' element={Login} />
</section>
</Routes>
</Fragment>
</Router>
);
}
error in console
[section] is not a <Route> component. All component children of <Routes> must be a <Route>
As the error is informing you, only Route or React.Fragment are valid children of the Routes component.
If you want to render several routed components into a specific layout, i.e. common UI/layout, then create a layout route for them to be nested into.
Make sure to also render Register and Login as JSX!
Example:
import { Outlet } from 'react-router-dom';
const SectionLayout = () => (
<section className='container'>
<Outlet /> // <-- nested routes render content here
</section>
);
export default SectionLayout;
...
import SectionLayout from '../path/to/SectionLayout';
...
<Routes>
<Route path='/' element={<Landing />} />
<Route element={<SectionLayout />}>
<Route path='/register' element={<Register />} />
<Route path='/login' element={<Login />} />
</Route>
</Routes>
For more information see:
Layout Routes
I think the error is quite descriptive in itself. That the children of <Routes /> can only be <Route /> and <section /> doesn't satisfy that.
If you need both Register and Login components to have a wrapper of section with .container class.
We can achieve it through different approaches, here are a few of them.
For eg.:
/**
* 1. Putting them inside the components itself
*/
const Register = () => {
return (
<section className="container">
// your other codes here
</section>
)
}
const Login = () => {
return (
<section className="container">
// your other codes here
</section>
)
}
/**
* 2. As a reusable Layout wrapper or Higher Order Component or
* Useful when you have many shared contents and styling
*/
const Container = (props) => {
return (
<section className="container">
// shared contents
{props.children}
// other shared contents
</section>
);
}
const Register = () => {
return (
<Container>
// your other codes here
</Container>
)
}
const Login = () => {
return (
<Container>
// your other codes here
</Container>
)
}
Hope that helps.

Cant access children in component used as element in Route (react-router-dom 6.3.0)

Im using react: 17.0.2 and react-router-dom: 6.3.0.
My current App.js snippet looks like this:
class App extends React.Component {
render() {
return (
<div className="App">
<Routes>
<Route element={<Layout/>}>
<Route path="/home" element={<Home/>} />
<Route path="/login" element={<Login/>} />
<Route path="/signup" element={<Signup/>} />
</Route>
<Route exact path='/' element={<Intro/>}/>
</Routes>
</div>
);
}
}
The route to '/' should not use the Layout component, so I need to exclude it from the context.
Within Layout.js I need to access the children like so:
const Layout = ({ children }) => {
console.log(children)
return (
<div>
<main>{children}</main>
</div>
);
};
But children are undefined in the above example. I can access children in Layout.js when I rewrite App.js to the below snippet, but then '/' is also rendered with the Layout.
class App extends React.Component {
render() {
return (
<div className="App">
<Layout>
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/login" element={<Login/>} />
<Route path="/signup" element={<Signup/>} />
<Route exact path='/' element={<Intro/>}/>
</Routes>
</Layout>
</div>
);
}
}
export default App;
How can I access children in Layout.js and render path '/' without the layout.
Route and Routes work a little differently in v6. The Route and React.Fragment components are the only valid children of the Routes component, and other Route components the only valid children of the Route. Layout routes must render an Outlet component for the nested routes to render their contents into.
import { Outlet } from 'react-router-dom';
const Layout = () => {
return (
<div>
<main>
<Outlet />
</main>
</div>
);
};

props.location is undefined with route component

i'm trying to run snippet code as below:
class App extends Component {
render() {
return (
<Router>
<div className="App">
<Navbar></Navbar>
<Routes>
<Route path="/" element={<Home></Home>} />
<Route path="/about" element={<About></About>} />
<Route path="/contact" element={<Contact></Contact>} />
<Route path="/challenges/*" element={<Challenges></Challenges>} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
);
}
}
let a = 0;
const Challenges = (props) => {
console.log(++a);
console.log(window.location.pathname);
const path = props.location.pathname;
const slug = path.split("/").slice(path.split("/").length - 1)[0];
const challenge = challenges.find((challenge) => challenge.slug === slug);
return (
<div>
<h1>30 Days Of React Challenge</h1>
<ul>
{challenges.map(({ name, slug }) => (
<li key={name}>
<NavLink to={`/challenges/${slug}`}>{name}</NavLink>
</li>
))}
</ul>
<Routes>
<Route
exact
path="/challenges"
element={<h1>Choose any of the challenges</h1>}
/>
<Route path={path} element={<Challenge challenge={challenge} />} />
</Routes>
</div>
);
};
i want to get the path at Challenges route component but it throw an error:
Cannot read properties of undefined (reading 'pathname')
i try to log variable "a" and "window.location" to test and it log two times like this:
1
/challenges
2
/challenges
My question is why i can't take value of props.location.pathname and why its run two times and the second time it throw an error why not at fisrt time.
Thank for helping me! Hope you have a good day.
Issue(s)
react-router-dom v6 Route components rendered via the element prop don't receive route props.
Route children components must use react hooks to access the route context, i.e. useParams, useLocation, useNavigate, etc... and therefore must be function components.
The console.log calls are in the function body so these are unintentional side-effects. This is likely why they are called twice, assuming the app is being rendered into a React.StrictMode component.
Solution
Challenges should use the uselocation hook to access the pathname. Move the console logs into an useEffect hook so they are called once per render to the DOM.
const Challenges = (props) => {
const { pathname } = useLocation();
useEffect(() => {
console.log(++a);
console.log(pathname);
});
const path = pathname;
const slug = path.split("/").slice(path.split("/").length - 1)[0];
const challenge = challenges.find((challenge) => challenge.slug === slug);
return (
<div>
<h1>30 Days Of React Challenge</h1>
<ul>
{challenges.map(({ name, slug }) => (
<li key={name}>
<NavLink to={`/challenges/${slug}`}>{name}</NavLink>
</li>
))}
</ul>
<Routes>
<Route
path="/challenges"
element={<h1>Choose any of the challenges</h1>}
/>
<Route path={path} element={<Challenge challenge={challenge} />} />
</Routes>
</div>
);
};
v6 api-reference

Creating routes inside a private route with react-router v6

Currently using react-router-dom 6.1.1 and I'm working with a private route.
Inside this private route I usually had other routes (so that I can keep my Sidebar on them).
My code looks like this
// App.tsx
const RequireAuth: React.FC<PrivateRouteProps> = ({ children, redirectTo }) => {
const isAuthenticated = Auth.isLogedIn()
return isAuthenticated ? children : <Navigate to={redirectTo} />
}
const Page = () => {
return (
<div className={css.host}>
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/"
element={
<RequireAuth redirectTo="/login">
<Home />
</RequireAuth>
}
/>
</Routes>
</BrowserRouter>
</div>
)
}
// Home/index.tsx
const Home = () => {
return (
<div className={css.host}>
<Sidebar sections={sidebarOptions(t)} />
<Routes>
{routes.map(({ breadCrumbtitle, link, component }, index) => (
<Route path={link} key={index}>
{component ? component : <p>[{breadCrumbtitle}] To be done</p>}
</Route>
))}
</Routes>
</div>
)
}
So... This setup worked with v5 but it seems to be something that doesn't really work with v6.
What can I do if I still want to keep the Sidebar for all the routes once I'm logged in?
I ended up finding the solution to my issue.
Doing what Drew Reese suggested only worked to a certain point since I was being led to a route that, for react router, didn't exist.
For it to work I add to do
// App.tsx
const RequireAuth: React.FC<PrivateRouteProps> = ({ children, redirectTo }) => {
const isAuthenticated = Auth.isLogedIn()
return isAuthenticated ? children : <Navigate to={redirectTo} />
}
const Page = () => {
return (
<div className={css.host}>
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path=""
element={
<RequireAuth redirectTo="/login">
<Home />
</RequireAuth>
}
>
{routes.map(({ breadCrumbtitle, link, component }, index) => {
return <Route path={link} key={index} element={component}></Route>
})}
</Route>
</Routes>
</BrowserRouter>
</div>
)
}
// Home/index.tsx
const Home = () => {
return (
<div className={css.host}>
<Sidebar sections={sidebarOptions(t)} />
<div className={css.contentContainer}>
<Outlet />
</div>
</div>
)
}
Using the Outlet seemed to be essential, don't know if it's something new on react router v6 but seemed to do the trick!
As far as I can tell the only issue is with the routes mapping, the Route components have invalid children, i.e. you are rendering another Route or React.Fragment as a child.
Move this up to the element prop of the mapped Route components.
const Home = () => {
return (
<div className={css.host}>
<Sidebar sections={sidebarOptions(t)} />
<Routes>
{routes.map(({ breadCrumbtitle, link, component }, index) => (
<Route
path={link}
key={index}
element={component || <p>[{breadCrumbtitle}] To be done</p>}
/>
))}
</Routes>
</div>
);
};

react-router v4 nesting routing issue

I have a following situation:
<Wrapper>
<Container>
<Route exact path="/" component={UserListing} />
<Route path="/user/:id" component={UserDetails} />
<Route exact path="(/|/user/\d+)" component={StaticComponent} />
</Container>
<Route path="/create-account" component={CreateAccount}/>
</Wrapper>
Okey, so my case is simple: I don't want the Container component to render if path does not equal to "/" or "/user/:id".
So if path is "/create-account" only Wrapper with CreateAccount as child should appear, without Container.
Looking for some help. Thank you
You can write a custom component that decides whether or not to render Container like
const RouterRenderer = ({ location, children }) => {
if(location && location.state && location.state.noMatch) {
return null;
}
return children;
}
And a sub component which tells this container that nothing matched like
const NoMatch = () => {
return <Redirect to={{state: {noMatch: true}}} />
}
export default withRouter(NoMatch);
Now you can use them like
<Wrapper>
<RouterRenderer>
<Container>
<Switch>
<Route exact path="/" component={UserListing} />
<Route path="/user/:id" component={UserDetails} />
<Route exact path="(/|/user/\d+)" component={StaticComponent} />
<NoMatch />
</Switch>
</Container>
</RouterRenderer>
<Route path="/create-account" component={CreateAccount}/>
</Wrapper>
P.S. Note that its important to use Switch when you are using NoMatch component since you want to render it only when no other
route matched. Also you can write RouteRenderer function in the form
of an HOC instead of a functional component.

Categories