I need to access the active path in the parent component, so that I can vary some CSS depending on the location. Here is App - the parent:
class App extends React.Component {
render(){
return (
<div className="wrapper" >
<div className="content">
<Router>
<div className="container_b">
<Menu />
<div>
<Route exact path="/" component={Home}/>
<Route path="/confidence" component={ConfidenceInterval}/>
<Route path="/proind" component={ProportionTestInd}/>
<Route path="/prodep" component={ProportionTestDep}/>
</div>
</div>
</Router>
<div className="push"></div>
</div>
<footer className="footer"><Footer /></footer>
</div>
)
}
}
I think for the App component, you have two options. First one is wrapping App with withRouter higher order component.
React-router provides a higher order component called withRouter, which passes match, history and location props to the component wrapped by it.
(for details please check withRouter)
In that case you can to the following
class App extends React.Component {
render(){
const {location: {pathname}} = this.props // pathname gives you the current path
return (
<div className="wrapper" >
<div className="content">
<Router>
<div className="container_b">
<Menu />
<div>
<Route exact path="/" component={Home}/>
<Route path="/confidence" component={ConfidenceInterval}/>
<Route path="/proind" component={ProportionTestInd}/>
<Route path="/prodep" component={ProportionTestDep}/>
</div>
</div>
</Router>
<div className="push"></div>
</div>
<footer className="footer"><Footer /></footer>
</div>
)
}
}
export const WrappedApp = withRouter(App)
Second option is, (again for App component) you can use window.location (please check window.location). location.pathname should also give you the current path in that case.
For other components, like Home,ConfidenceInterval etc, Route passes match, history and location props by default so you can make use of them.
import {useLocation} from 'react-router-dom'
const App = () => {
const location = useLocation();
console.log(location.pathname);
enter code herereturn <span>Path : {location.pathname}</span>
}
Related
I have a component included in my App.js and that component contains some common logic which needs to be executed.
So my App component JSX looks like
<CommonComponent />
{canRenderBool && (
<div class="container">
<Route exact path="/comp1">
<Comp1 />
</Route>
<Route exact path="/comp2">
<Comp2 />
</Route>
</div>
)
}
Now, I want that on each route transition (e.g. user clicks on a new route url), I want that the code in CommonComponent (non-routable component) gets triggered.
This common component has logic which returns a boolean variable and I kind of render/not render Comp1/Comp2 i.e. all the routes based on that boolean
What is the best way I can handle it. I want to avoid having that code defined/invoked in each component manually?
Also most of my components are functional/hooks based.
In case you are using functional component for your App Component. You can write your CommonComponent logic inside useEffect and then there you can set state for your canRenderBool flag
for example
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Switch, Route, Link, useLocation } from "react-router-dom";
function App() {
const [canRenderBool , setCanRenderBool] = useState(false);
const location = useLocation(); // this will only work if you wrap your App component in a Router
// this effect will run on every route change
useEffect(() => {
// your logic here
// ..
// set value of 'canRenderBool' flag
setCanRenderBool(true);
}, [location]);
return (
// Note that Router is not used here it will be wraped when we export this component, see below
<Switch>
<Route exact path="/">
<Page1 />
</Route>
{canRenderBool && (
<Route exact path="/page-2">
<Page2 />
</Route>
)}
</Switch>
);
}
function Page1() {
return (
<div>
<h1>
Go to <Link to="/page-2">Page 2</Link>
</h1>
</div>
);
}
function Page2() {
return (
<div>
<h1>
Go to <Link to="/">Page 1</Link>
</h1>
</div>
);
}
// Wrapping Router around App and exporting
export default () => (
<Router>
<App />
</Router>
);
{canRenderBool && (
<Router>
<div class="container">
<Switch>
<Route exact path="/comp1"> <Comp1 /> </Route>
<Route exact path="/comp2"> <Comp2 /> </Route>
</Switch>
<Link to="/comp1">Component 1</Link>
<Link to="/comp2">Component 2</Link>
</div>
</Router>
)
}
This might help. And you will need to import all keywords and components before using them.
I am trying to use nested routes to render different components. When I click my links, URL does update but the components are not rendering. Prior to this I was using imported components, but since that wasn't working, I stripped it down to this block of code and it's still just showing a blank component and no errors.
import React from 'react';
import { Route, Switch, Link, useRouteMatch } from 'react-router-dom';
function InfluencerComponent() {
let { path, url } = useRouteMatch();
const navLinks = (
<div>
<Link to={`${url}/select-trade`}>Select trade</Link>
<Link to={`${url}/add-skills`} className="ml-2">
Add skills
</Link>
</div>
);
return (
<div className="row mt-3">
<Switch>
<Route exact path={path}>
{navLinks}
</Route>
<Route path={`${path}/select-trade`}>
{navLinks}
<Test />
</Route>
<Route path={`${path}/add-skills`}>
{navLinks}
<TestTwo />
</Route>
</Switch>
</div>
);
}
function Test() {
return 'Test Component';
}
function TestTwo() {
return 'Another Test Component';
}
export default InfluencerComponent;
Components are not rendering because you should use component prop instead of children.
Example:
return (
<div className="row mt-3">
<Switch>
// ...
<Route path={`${path}/add-skills`} component={<>{navLinks}<TestTwo /></>} />
</Switch>
</div>
);
More info about <Route /> props:
https://reacttraining.com/react-router/web/api/Route/component
I am new to React and trying to create a layout with nested routes. Here's my scenario
show Login when URL is /
show Dashboard when URL is /dashboard
show Profile when URL is /dashboard/profile (this should load
inside the dashboard content area)
The login page and dashboard page are loading properly when the URL is accessed in the browser but for /dashboard/profile, the browser goes to a blank page instead of loading it inside the dashboard component.
Index.js
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'));
App.js
class App extends Component {
render() {
return (
<div>
{/* <Switch> */}
<Route exact path='/' component={SignIn}/>
<Route exact path='/dashboard' component={Dashboard}/>
{/* </Switch> */}
</div>
);
}
}
export default App;
Dashboard.js
class Dashboard extends React.Component {
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.root}>
<Header classes={classes} open={this.state.open} click={this.handleDrawerOpen} />
<Sidebar classes={classes} open={this.state.open} click={this.handleDrawerClose} />
<main className={classes.content}>
<div className={classes.appBarSpacer} />
*********I expect profile component to load here
but when I access the URL /dashboard/profile I get a new blank page*********
Route path="/dashboard/profile" exact component={Profile} />
</main>
</div>
</React.Fragment>
);
}
}
You need to remove the exact prop from the Dashboard route (present in Switch) while doing the child routing.
This is the minimal implementation of your use case:
import React, { Component } from "react";
import "./styles.css";
import {
NavLink,
Redirect,
Route,
BrowserRouter as Router,
Switch
} from "react-router-dom";
const App = () => (
<Router>
<div className="App">
<ul>
<li>
<NavLink to="/login">Login</NavLink>
</li>
<li>
<NavLink to="/dashboard">Dashboard</NavLink>
</li>
</ul>
<Switch>
<Route exact path="/login" component={Login} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</div>
</Router>
);
const Login = () => <span>Login Page</span>;
const Dashboard = () => {
return (
<div>
<div>Dashboard Page</div>
<NavLink to="/dashboard/profile">Go to profile</NavLink>
<div>
<Route exact path="/dashboard/profile" component={Profile} />
</div>
</div>
);
};
const Profile = () => {
return <span>Profile Page</span>;
};
export default App;
You can find the working example here:https://codesandbox.io/s/z3py3672v3
I have a simple App that uses BrowserRouter from 'react-router-dom' v4. I'm trying to access the location.pathname property from within the <BrowserRouter/> component, without avail:
class App extends Component{
render(){
return (
<BrowserRouter>
// How do I access this.props.location?
<div className={(this.props.location.pathnme === "/account") ? "bgnd-black" : "bgnd-white"} >
<Switch>
<Route path="/login" component={LoginPage}/>
<Route path="/success" component={LoginSuccess}/>
<Route path="/account" component={MyAccount}/>
...
<Route component={Error404}/>
</Switch>
</div>
</BrowserRouter>
);
}
}
I know that I can access the app's current path location through the child components with this.props.location.pathname, but I need to access it from the parent component, just below <BrowserRouter/> to run additional logic that doesn't pertain to child components. How can I get this location?
You can also do it using withRouter which has a similar result to putting the code in a render parameter and avoids the need for a "fake" <Route/>.
Essentially you put the JSX that needs to know the location in a component of its own, which is wrapped by withRouter. This supplies the location to the component:
import { withRouter } from 'react-router-dom';
const Content = withRouter(props =>
<div className={(props.location.pathname === "/account") ? "backg...
...
</div>
);
Then you use that in your main router section:
class App extends Component{
render() {
return (
<BrowserRouter>
<Content/>
...
Since react-router v5.1.0 you can use useLocation.
https://reactrouter.com/web/api/Hooks/uselocation
class App extends Component{
render(){
const location = useLocation();
return (
<div className={(location.pathname === "/account") ? "bgnd-black" : "bgnd-white"} >
//...
</div>
);
}
}
// ...
<BrowserRouter>
<App />
</BrowserRouter>
After digging through their GitHub issues, I found the solution. I must render a <Route /> within <BrowserRouter /> and pass the rest of my app into its render() function with history as a parameter. Within the render function, I can find the app's location in history.location.pathname.
class App extends Component{
render(){
return (
<BrowserRouter>
// We must add a parent <Route> and render its children while passing 'history' as parameter
<Route path={Paths.reserve} render={(history) =>
// Within render(), we can find it in history.location.pathname
<div className={(history.location.pathname === "/account") ? "background-black" : "background-white"} >
<Switch>
<Route path="/login" component={LoginPage}/>
<Route path="/success" component={LoginSuccess}/>
<Route path="/account" component={MyAccount}/>
...
<Route component={Error404}/>
</Switch>
</div>
}/>
}} />
</BrowserRouter>
);
}
}
This will update the history parameter automatically, without having to re-render on componentDidMount() or componentDidUpdate()
You achieve what u have asked for by doing this
import AccessRoute from './AccessRoute'
class App extends Component{
render(){
return (
<BrowserRouter>
<AccessRoute>
<div className={(this.props.location.pathnme === "/account") ? "bgnd-black" : "bgnd-white"} >
<Switch>
<Route path="/login" component={LoginPage}/>
<Route path="/success" component={LoginSuccess}/>
<Route path="/account" component={MyAccount}/>
...
<Route component={Error404}/>
</Switch>
</div>
</AccessRoute>
</BrowserRouter>
);
}
}
AccessRoute.jsx
import React from 'react'
import {withRouter} from 'react-router';
class AccessRoute extends React.Component{
constructor(props){
super(props);
}
//If you want to find the location on mount use this
componentDidMount(){
console.log("the path name is ",this.props.location.pathname);
}
//If you want to find the location on change use this
componentDidUpdate(prevprops){
if(this.props.location.pathname!=prevprops.location.pathname){
console.log("the new path name is ",this.props.location.pathname);
}
}
render(){
return(
this.props.children
);
}
}
export default withRouter(AccessRoute)
Building my first React app and I am trying to create a nested router within the authenticated 'internal' page but it's proving quite difficult. I've tried a number of things, but this is the latest incarnation--and I am getting a cannot read 'children' property of undefined error in the AuthenticatedFrame component.
export class NavigationFrame extends React.Component<INavigationFrameProps, {}> {
render() {
console.log(this.props)
console.log(this.state)
console.log(this.props.store.store.getState())
return <Router>
<div className="navigation-frame">
<Route exact path="/" component={Splash}/>
<Route exact path="/register" component={RegistrationForm}/>
<Route path="/signin" component={SignInContainer}/>
<Route path="/i" render={() => (
this.props.store.store.getState().authenticationState.authenticated ? (
<AuthenticatedFrame>
<Route exact path="/i/" component={WelcomePage}/>
<Route path="/i/add" component={AddPage}/>
</AuthenticatedFrame>
) : (
<Redirect to="/signin"/>
)
)}/>
</div>
</Router>;
}
}
And my AuthenticatedFrame (doesn't work)
export const AuthenticatedFrame = () => (
<div className="internal-view">
<SidebarDrawer />
<div className="content-area">
{this.props.children}
</div>
</div>)
AuthenticatedFrame is a functional component and hence doesn't have access to this keyword, you need to access props as function arguments like
export const AuthenticatedFrame = (props) => (
<div className="internal-view">
<SidebarDrawer />
<div className="content-area">
{props.children}
</div>
</div>
)
Urf. I just had to change out Authenticated Frame with this:
export class AuthenticatedFrame extends React.Component<{children: any}, {}> {
render() { return <div className="internal-view">
<SidebarDrawer />
<div className="content-area">
{this.props.children}
</div>
</div>}