React Router confusion - javascript

New to react & react-router.
I'm trying to understand this example:
https://github.com/ReactTraining/react-router/blob/1.0.x/docs/API.md#components-1
But this.props never contains main or sidebar. My code:
Main.js
ReactDOM.render(
<Router>
<Route path="/" component={App2}>
<Route path="/" components={{main: Home, sidebar: HomeSidebar}}/>
</Route>
</Router>,
document.getElementById('content')
);
App2.js
class App2 extends React.Component {
render() {
const {main, sidebar} = this.props;
return (
<div>
<Menu inverted vertical fixed="left">
{sidebar}
</Menu>
<Container className="main-container">
{main}
</Container>
</div>
);
}
}
export default App2;
Home.js
import React from 'react';
class Home extends React.Component {
render() {
return (
<div><h1>Home</h1></div>
);
}
}
export default Home;
HomeSidebar.js
class HomeSidebar extends React.Component {
render() {
return (
<div>
<p>I'm a sidebar</p>
</div>
);
}
}
export default HomeSidebar;
I'm using electron with react dev tools. Whenever I debug, this.props contains neither main nor sidebar. Any idea why this is happening?
I've also tried using an IndexRoute, but it seems to not support a components prop.
Other things I've tried
ReactDOM.render(
<Router>
<Route component={App2}>
<Route path="/" components={{main: Home, sidebar: HomeSidebar}}/>
</Route>
</Router>,
document.getElementById('content')
);
ReactDOM.render(
<Router>
<Route path="/" component={App2} components={{main: Home, sidebar: HomeSidebar}}>
<Route path="admin" components={{main: Admin, sidebar: AdminSidebar}}/>
</Route>
</Router>,
document.getElementById('content')
);

Looks like to have the components prop work you need use the <IndexRoute /> component instead of <Route />. In the react-router docs it mentions that IndexRoute has all of the same props as Route so doing
<IndexRoute components={{main: Main, side: Side}} />
works!
Full code:
React.render((
<Router>
<Route path="/" component={App} >
<IndexRoute components={{main: Main, side: Side}} />
</Route>
</Router>
), document.getElementById('app'))
Codepen: http://codepen.io/chmaltsp/pen/ZeLaPr?editors=001
Cheers!

If you're using the current version of react-router (v4.0.0), it looks like they did away with the components prop on Routes: https://reacttraining.com/react-router/web/api/Route
You can render Routes anywhere, and they even have a sidebar example where they do just that. They have one set of Route components to render the main components and another set of Route components for sidebars, but both come from a single route config to keep it DRY.
To translate that to your code, you could create a route config:
const routes = [
{ path: '/',
sidebar: Sidebar
main: Main
}
];
Then in Main.js
ReactDOM.render(
<Router>
<Route component={App2}>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
component={route.main}
/>
))}
</Route>
</Router>,
document.getElementById('content')
);
Then in App2.js
class App2 extends React.Component {
render() {
return (
<div>
<Menu inverted vertical fixed="left">
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
component={route.sidebar}
/>
))}
</Menu>
<Container className="main-container">
{this.props.children}
</Container>
</div>
);
}
}
export default App2;

The example from github was written 2 years ago (look here) and I'am not sure for which particular version it is related. And I'am not sure does it works now (because I am also new with react), but I know that you don't have use this approach to reach this aim, you can use separated component which will contains mainz and sidebar, here my example:
class App2 extends React.Component {
render() {
return (
// Your Menu.
// Your Container.
<div>
{this.props.children}
</div>
);
}
}
class Home extends React.Component {
render() {
return (<div><h1>Home</h1></div>);
}
}
class HomeSidebar extends React.Component {
render() {
return (<div><p>I am a sidebar</p></div>);
}
}
class HomeWithSidebar extends React.Component {
render() {
return (
<div>
<Home />
<HomeSidebar />
</div>
);
}
}
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App2}>
<Route path="/a2" components={HomeWithSidebar} />
</Route>
</Router>,
document.getElementById('content')
);
PS: Don't forget use <Router history={browserHistory}> in your example.
And use IndexRoute in your example or specify another, like /a2 in my example.

Im new to react router myself but I am sure that the routes you are using are incorrect. in the example you give you have 2 different routes that resolve to the same path (/):
ReactDOM.render(
<Router>
<Route path="/" component={App2}>
<Route path="/" components={{main: Home, sidebar: HomeSidebar}}/>
</Route>
</Router>,
document.getElementById('content')
);
I beleieve this should be something like:
ReactDOM.render(
<Router>
<Route path="/" component={App2}>
<IndexRoute components={{main: Home, sidebar: HomeSidebar}}/>
<Route path="some/other/path" components={{main: Home, sidebar: SomeOtherSidebar}}/>
</Route>
</Router>,
document.getElementById('content')
);

I hope you know that you are using react-router 1.0.x and it is quite outdated. Current version is 4.x.
Below code works perfectly( based on your example provided).
let Router = ReactRouter.Router;
let RouterContext = Router.RouterContext;
let Route = ReactRouter.Route;
class App2 extends React.Component {
render () {
const { main, sidebar } = this.props;
return (
<div>
<div className="Main">
{main}
</div>
<div className="Sidebar">
{sidebar}
</div>
</div>
)
}
}
class Home extends React.Component {
render() {
return (
<div><h1>Home</h1></div>
);
}
}
class HomeSidebar extends React.Component {
render() {
return (
<div>
<p>I'm a sidebar</p>
</div>
);
}
}
ReactDOM.render(
<Router>
<Route component={App2}>
<Route path="/" components={{main: Home, sidebar: HomeSidebar}}/>
</Route>
</Router>,
document.getElementById('content')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router/1.0.3/ReactRouter.min.js"></script>
<div id="content"></div>

Related

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>
);
};

I'm getting blank react-app page on using Route

I'm having some kind of trouble when I'm using Router in App.js
I'm getting a blank page when I am using, I tried a lot but couldn't found a way to solve the issue.
<GuestRoute path="/Authenticate" element={<Authenticate />}>
</GuestRoute>
it is working fine with
<Route path="/Authenticate" element={<Authenticate />}>
</Route>
but I have to use GuestRoute.
Given below is the whole code:
App.js
import "./App.css";
import {BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import React from "react"
import Navigation from "./components/shared/Navigation/Navigation";
import Home from "./Pages/Home/Home";
import Register from "./Pages/Register/Register";
import Login from "./Pages/Login/Login";
import Authenticate from "./Pages/Authenticate/Authenticate";
const isAuth = true;
function App() {
return (
<div className="App">
<Router>
<Navigation />
{/* switch(prev. versions) ----> Routes (new versions)) */}
<Routes>
<Route exact path="/" element={<Home />} >
</Route>
<GuestRoute path="/Authenticate" element={<Authenticate />}>
</GuestRoute>
</Routes>
</Router>
</div>
);
}
const GuestRoute = ({children,...rest}) => {
return(
<Route {...rest}
render={({location})=>{
return isAuth ? (
<Navigate to={{
pathname: '/rooms',
state: {from: location}
}}
/>
):(
children
);
}}
></Route>
);
};
export default App;
react-router-dom#6 doesn't use custom route components. The new pattern used in v6 are either wrapper components or layout route components.
Wrapper component example:
const GuestWrapper = ({ children }) => {
... guest route wrapper logic ...
return (
...
{children}
...
);
};
...
<Router>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/Authenticate"
element={(
<GuestWrapper>
<Authenticate />
</GuestWrapper>
)}
/>
</Routes>
</Router>
Layout route component example:
import { Outlet } from 'react-router-dom';
const GuestLayout = () => {
... guest route wrapper logic ...
return (
...
<Outlet /> // <-- nested routes render here
...
);
};
...
<Router>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route element={<GuestLayout>}>
<Route path="/Authenticate" element={<Authenticate />} />
... other GuestRoute routes ...
</Route>
</Routes>
</Router>

Nested index route not rendering in react-router-dom v6

I have a create-react-app project with react-router-dom v6 installed. Trying to use the new index route syntax so that my HomePage component renders at the index that is currently serving a Layout component. When I navigate to the index (http://localhost:3000/), it renders the Layout component with site name in an but not the HomePage component (The "Home Page" does not render).
Thanks for the help!
Code below:
App.js
import './App.css';
import {Routes, Route, Outlet, Link, BrowserRouter as Router} from "react-router-dom";
import Layout from "./components/layout/Layout";
import HomePage from "./pages/Home";
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} />
</Route>
</Routes>
</Router>
<Outlet />
</div>
);
}
export default App;
Home.js
const HomePage = () => {
return (
<div>
<h1>Home Page</h1>
</div>
)
}
export default HomePage
Layout.js
import data from "../../config/siteconfig.json"
const settings = data.settings;
const Layout = ({children}) => {
return (
<div>
<h1>{settings.sitename}</h1>
{children}
</div>
)
}
export default Layout
If you want nested Route components to render then the Layout component should render an Outlet for them to be rendered into. Using children prop would be if Layout was directly wrapping children components.
In other words, it is the difference between
<Route
path="/"
element={(
<Layout>
<HomePage /> // <-- rendered as children
</Layout>
)}
/>
and
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} /> // <-- rendered as nested route
</Route>
Suggested code update:
import { Outlet } from 'react-router-dom';
const Layout = ({children}) => {
return (
<div>
<h1>{settings.sitename}</h1>
<Outlet />
</div>
);
};
...
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} />
</Route>
</Routes>
</Router>
</div>
);
}

issue with routing in react

I am facing this routing issue where once the user hits /home ,it gets redirected to another route /home/render and things are fine .
However ,if one hits home/live ,I don't see that it renders Component1 which is desired .
Here is the link to the codesandbox .
sandbox link
Here is my main Component index.js
import App from "./Components/App";
import Home from "./Components/Home";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
class Parent extends React.Component {
render() {
return (
<div>
<Router>
<Switch>
<Route exact path="/" component={Page} />
<Route exact path="/home" component={App} />
<Route path="/home/render" component={Home} />
</Switch>
</Router>
</div>
);
}
}
class Page extends React.Component {
render() {
return <div>Page</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
and this is my App.js
class App extends React.Component {
render() {
const { match } = this.props;
console.log("match.url", match.url);
console.log("match.path", match.path);
return (
<div>
<h1>App</h1>
<Switch>
<Redirect from={match.url} exact to={match.url + "/render"} />;
<Route
path={match.path + "/live"}
render={routeProps => (
<Component1
matchBaseUrl={match.url}
{...routeProps}
matchId={100}
/>
)}
/>
</Switch>
</div>
);
}
}
I didn't put component.js because that's just a simple component I am rendering on screen when we hit home/live .
Component1 is never rendered because you set the exact prop to true on the /home route, which will make it so it only matches if the path is /home and nothing more.
You can remove the exact prop and move the route below the /home/render route so that takes precedence and it wlll work as expected.
<Switch>
<Route exact path="/" component={Page} />
<Route path="/home/render" component={Home} />
<Route path="/home" component={App} />
</Switch>

Why this pass props into this.props.children doesn't work?

I tried to pass in props to this.props.children using the following code
export default class Home extends Component {
render(){
var children = React.Children.map(this.props.children, function (child) {
return React.cloneElement(child, {
foo: "1"
})
});
return(
<div className="bla">
<h1>WeDate</h1>
<div className="child">
{children}
</div>)
}
}
But I can't read this.props.foo in my searchDate component when it renders normally.
The following is my react router.
render(
<Router>
<Home>
<Switch>
<Route exact path="/"><Redirect to="/search" push/></Route>
<Route exact path="/search" component={SearchDate}></Route>
</Switch>
</Home>
</Router>
,document.getElementById('app')
);
the children to your Home component are not the Routes but the Switch and hence foo is not passed down as props to the respective components. What you need to do is to nest your Routes is the Home component and not as children
Home
export default class Home extends Component {
render(){
return(
<div className="bla">
<h1>WeDate</h1>
<div className="child">
<Switch>
<Redirect from="/" exact to="/search"/>
<Route exact path="/search" render={(props) => <SearchDate foo={'1'} {...props}/>}>
</Switch>
</div>)
}
}
Routes
render(
<Router>
<Home />
</Router>
,document.getElementById('app')
);

Categories