I am building a small project to test the React Router 4. So far so good, my url updates and my props.locations shows up with withRouter. But I can't seem to change my navBar base on the props.location.
This is what my Routes look like:
<Provider store={ store }>
<BrowserRouter onUpdate={() => window.scrollTo(0, 0)}>
<div className="root">
<App/>
<Switch>
<Route exact path="/" component={HomePageContainer}/>
<Route eact path="/signin" component={SignInContainer}/>
<Route eact path="/reviews" component={Reviews}/>
<Route path="/favorites" component={Favorites}/>
<Route render={() => (
<p>Page Not Found</p>
)}/>
</Switch>
</div>
</BrowserRouter>
</Provider>
My component basically contains my HeaderBar and navBar, I have messages thats in navBar that I want to change so I would have title of the page, My App looks like this:
const App = (props) => {
let toRender = null;
if(props.location.pathname !== '/signin'){
toRender = (
<div>
<HeaderContainer />
<NavBarContainer />
</div>
);
} else {
toRender = null;
}
return(
<div className="App">
{ toRender }
</div>
);
}
I can import my navBar container into each of the routes i have for '/', '/reviews', and '/favorites'. But I don't think that would be a modular way to do it. I also have a shouldComponentUpdate lifecycle method inside NavBar, and I tested with a console.log to print something when it does update when I switch url, but it doesn't. Does anyone have any suggestions on a clean solution to pass in the props to my NavBar without importing it into every single one of the components? I also tried putting App component in the place of Route so I would have:
<App exact path="/" component={HomePageContainer}/>
<Route eact path="/signin" component={SignInContainer}/>
<App eact path="/reviews" component={Reviews}/>
<App path="/favorites" component={Favorites}/>
But then my Components aren't rendering besides the App. I'm not sure what's happening or why it's not rendering the components. Any suggestions would be much appreciate it. Thank you.
Related
Recently I started learning React and my problem here is that I cannot hide Navbar when Im in Login page/component. I have a Router in index.js like this:
const routing = (
<div>
<NavBar />
<Router>
<div>
<Switch>
<Route exact path="/" component={App} />
<Route path="/users/:id" component={Users} />
<Route path="/users" component={Users} />
<Route path="/contact" component={Contact} />
<Route path="/login" component={LoginPage} hideNavBar={true} />
<Route component={Notfound} />
</Switch>
</div>
</Router>
</div>
)
ReactDOM.render(routing, document.getElementById('root'));
From the little search that I made most approaches were like inserting <NavBar /> in every component and use a flag to hide it when im in Login. Is there any other way like modifying the above code simple and fast?
Not really you could have restrict access to all other routes like below I suppose
const PrivateRoute = ({component: Component, ...rest }) =>
<Route
{...rest}
render={props =>
authenticated ? <Container ><Component {...props} /> <Container /> : <Redirect to="/login" />
}
/>
};
Where you container has a header , maybe footer and takes a child prop (your component)
There are better approaches for hiding the NavBaron authenticated routes, but if you want to hide it when it's on foo route, you could check the path name and decide to render it or not.
import { useLocation } from "react-router-dom";
const NavBar = () => {
const location = useLocation()
return location.pathname == '/login' ? <YourNavBarComponents /> : null
}
The best way to hide parts of the UI building with React is to not include the markup. In order to do NOT display parts relying on the authentication you should have a flag if the authentication has done and is successful.
For example you can build a component that share some context in order to check if the authentication flag is true and render the parts that must be available only to users who have logged in.
You can follow this example to see the details how to build that component.
Basically you have a component that wraps another component and based on some rules render its children or call a render prop:
function Private({ shouldRender = false, children }) {
return shouldRender
? children
: null;
}
function App() {
return (
<div>
<h2>Application</h2>
<Private><div>This part is hidden</div></Private>
<Private shouldRender={true}><div>This part is <strong>available</strong></div></Private>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
In my React Application I need to reload the component when it comes to the path path="/". I know react-router helps us reload easily the current component but I really need this in my application for some styling purpose. In my application I have two paths:
<Route path="/" component={newRoute}/>
and <Route path="/gallery" component={GalleryImages}/>. So, whenever I move from GalleryImages to newRoute I need to reload the newRoute components otherwise the styles are not working. What is the way around here? Here's myApp.js now:
const newRoute = () => {
return (
<div id="colorlib-page">
<div id="container-wrap">
<div id="colorlib-main">
<Introduction/>
<About/>
<Projects/>
<Timeline/>
<Blog/>
</div>
</div>
</div>
)
}
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Sidebar/>
<Switch>
<Route path="/" component={newRoute} exact/>
<Route path="/gallery" component={GalleryImages} exact/>
<Route component={Error}/>
</Switch>
</div>
</BrowserRouter>
);
}
}
export default App;
Try to use class component instead of functional component
Please read this properly before marking as duplicate, I assure you I've read and tried everything everyone suggests about this issue on stackoverflow and github.
I have a route within my app rendered as below;
<div>
<Header compact={this.state.compact} impersonateUser={this.impersonateUser} users={users} organisations={this.props.organisations} user={user} logOut={this.logout} />
<div className="container">
{user && <Route path="/" component={() => <Routes userRole={user.Role} />} />}
</div>
{this.props.alerts.map((alert) =>
<AlertContainer key={alert.Id} error={alert.Error} messageTitle={alert.Error ? alert.Message : "Alert"} messageBody={alert.Error ? undefined : alert.Message} />)
}
</div>
The route rendering Routes renders a component that switches on the user role and lazy loads the correct routes component based on that role, that routes component renders a switch for the main pages. Simplified this looks like the below.
import * as React from 'react';
import LoadingPage from '../../components/sharedPages/loadingPage/LoadingPage';
import * as Loadable from 'react-loadable';
export interface RoutesProps {
userRole: string;
}
const Routes = ({ userRole }) => {
var RoleRoutesComponent: any = null;
switch (userRole) {
case "Admin":
RoleRoutesComponent = Loadable({
loader: () => import('./systemAdminRoutes/SystemAdminRoutes'),
loading: () => <LoadingPage />
});
break;
default:
break;
}
return (
<div>
<RoleRoutesComponent/>
</div>
);
}
export default Routes;
And then the routes component
const SystemAdminRoutes = () => {
var key = "/";
return (
<Switch>
<Route key={key} exact path="/" component={HomePage} />
<Route key={key} exact path="/home" component={HomePage} />
<Route key={key} path="/second" component={SecondPage} />
<Route key={key} path="/third" component={ThirdPage} />
...
<Route key={key} component={NotFoundPage} />
</Switch>
);
}
export default SystemAdminRoutes;
So the issue is whenever the user navigates from "/" to "/second" etc... app re-renders Routes, meaning the role switch logic is rerun, the user-specific routes are reloaded and re-rendered and state on pages is lost.
Things I've tried;
I've tried this with both react-loadable and React.lazy() and it has the same issue.
I've tried making the routes components classes
Giving all Routes down the tree the same key
Rendering all components down to the switch with path "/" but still the same problem.
Changing Route's component prop to render.
Changing the main app render method to component={Routes} and getting props via redux
There must be something wrong with the way I'm rendering the main routes component in the app component but I'm stumped, can anyone shed some light? Also note this has nothing to do with react-router's switch.
EDIT: I've modified one of my old test project to demonstrate this bug, you can clone the repo from https://github.com/Trackerchum/route-bug-demo - once the repo's cloned just run an npm install in root dir and npm start. I've got it logging to console when the Routes and SystemAdminRoutes are re-rendered/remounted
EDIT: I've opened an issue about this on GitHub, possible bug
Route re-rendering component on every path change, despite path of "/"
Found the reason this is happening straight from a developer (credit Tim Dorr). The route is re-rendering the component every time because it is an anonymous function. This happens twice down the tree, both in App and Routes (within Loadable function), below respectively.
<Route path="/" component={() => <Routes userRole={user.Role} />} />
needs to be
<Routes userRole={user.Role} />
and
loader: () => import('./systemAdminRoutes/SystemAdminRoutes')
Basically my whole approach needs to be rethought
EDIT: I eventually fixed this by using the render method on route:
<Route path="/" render={() => <Routes userRole={user.Role} />} />
Bumped into this problem and solved it like this:
In the component:
import {useParams} from "react-router-dom";
const {userRole: roleFromRoute} = useParams();
const [userRole, setUserRole] = useState(null);
useEffect(()=>{
setUserRole(roleFromRoute);
},[roleFromRoute]}
In the routes:
<Route path="/generic/:userRole" component={myComponent} />
This sets up a generic route with a parameter for the role.
In the component useParams picks up the changed parameter und the useEffect sets a state to trigger the render and whatever busines logic is needed.
},[userRole]);
Just put the "/" in the end and put the other routes above it.
Basically it's matching the first available option, so it matches "/" every time.
<Switch>
<Route key={key} exact path="/home" component={HomePage} />
<Route key={key} path="/second" component={SecondPage} />
<Route key={key} path="/third" component={ThirdPage} />
<Route key={key} exact path="/" component={HomePage} />
<Route key={key} component={NotFoundPage} />
</Switch>
OR
<Switch>
<Route path="/second" component={SecondPage} />
<Route exact path="/" component={HomePage} />
<Route path="*" component={NotFound} />
</Switch>
Reorder like this, it will start working.
Simple :)
I have a working React/Redux Router app, implemented with react-router-dom 4.2.2 and react-router-redux 5.0.0-alpha.9.
I'd like to add the basename /calendar to all paths though, via BrowserRouter's basename API that looks like this:
<BrowserRouter basename="/calendar"/>
<Link to="/today"/> // renders <a href="/calendar/today">
But this far I haven't had any use for BrowserRouter, so I don't knwo really where it should go.
I have tried to encapsulate (+ put it next to as in the example above) the NavLinks as well as the Routes with BrowserRouter, but that doesn't seem to do anything.
As my app works fine without BrowserRouter - is it an optional part of react-router or is it already integrated somehow in react-router-redux?
Update
This is how I've tried to encapsulate NavLink and/or Route with BrowserRouter:
const ComponentA = () => (
<div>
<BrowserRouter basename='/calendar'>
<NavLink exact to="/a">Home</NavLink>
<NavLink to="/about">About</NavLink>
</BrowserRouter>
</div>
)
const ComponentB = () => (
<div>
<BrowserRouter basename='/calendar'>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
</Switch>
</BrowserRouter>
</div>
)
This is how I solved it:
export default class Root extends Component {
render() {
const { store, history } = this.props;
return (
<Provider store={store}>
<ConnectedRouter history={history}>
<BrowserRouter basename='/calendar'>
<App />
</BrowserRouter>
</ConnectedRouter>
</Provider>
);
}
}
Is there a way to nest routes in React Router v4?
This works:
<Router basename='/app'>
<main>
<Route path='/' component={AppBar} />
<Route path='/customers' component={Customers} />
</main>
</Router>
This does not:
<Router basename='/app'>
<Route path='/' component={AppBar}>
<Route path='/customers' component={Customers} />
</Route>
</Router>
Customers Component:
import React, { Component, PropTypes } from 'react'
import styled from 'styled-components'
export default class Customers extends Component {
render () {
return (
<Container>
<h1>Customers</h1>
</Container>
)
}
}
const Container = styled.section`
height: 100%;
padding: 15px;
overflow: auto;
`
Best pattern I have found so far.
// main app
<div>
// not setting a path prop, makes this always render
<Route component={AppShell}/>
<Switch>
<Route exact path="/" component={Login}/>
<Route path="/dashboard" component={AsyncDashboard(userAgent)}/>
<Route component={NoMatch}/>
</Switch>
</div>
I can just keep nesting this inside a component and everything works nice including hmr(If using webpack, dont forget to set output.publicPath to "/")
// dashboard component
<div>
// the same way as before, not setting a path prop
// makes it render on every /dashboard/** request
<Route component={DashboardTAB}/>
<Switch>
// longer path (with same root) than others first
<Route path="/dashboard/graphs/longerpath" component={GraphForm}/>
<Route path="/dashboard/graphs" component={Graphs}/>
<Route path="/dashboard/workers" component={List}/>
<Route path="/dashboard/insert" component={InsertComponent}/>
</Switch>
</div>
I adapted this from the docs, seem to work so far. Probably missing something obvious, and yes it is not the v4 way but we need all the routes defined in one place.
function RouteNest(props){ return (
<Route exact={props.exact} path={props.path} render={ p => <props.component {...p} children={props.children}/> } />
)}
export const MainRoutes = props =>
<div className='content layout'>
<Route exact path="/" component={Landing}/>
<Route path={'/contact'} component={Contact}/>
<RouteNest path={'/thing'} component={CompoWithSub}>
<RouteNest path={'/thing/suba'} component={SubComponentA}/>
<RouteNest path={'/thing/subb'} component={SubComponentB}/>
</RouteNest>
</div>
export const CompoWithSub = props => <div>{props.children)</div>
You're AppBar component is in charge of rendering Customers. For customers to be called, you have to render the children of AppBar. Anything directly nested under AppBar is a child of AppBar.
import React from 'react';
const AppBar = ({ children }) => (
<div>
<header>
<h1> stuff </h1>
</header>
{children}
</div>
);
export default AppBar
Please note that only AppBar will render when you visit "/". AppBar and Customers will render when you visit "/customers".
If someone wants to have nested routes without typing prefix of wrapper route I've created something like this in TSX:
Imports:
import * as React from 'react';
import { Route, RouteComponentProps, RouteProps, Switch } from 'react-router-dom';
import Index from 'views/index';
import Login from 'views/login';
import NoMatch from 'views/no-match';
Interfaces:
interface INestedRoutes {
nested?: string;
}
interface INestedRoute extends RouteProps, INestedRoutes {}
NestedRoute and NestedRoutes wrapper:
class NestedRoutes extends React.Component<INestedRoutes> {
public render() {
const childrenWithProps = React.Children.map(this.props.children, (child) => {
return React.cloneElement(
child as React.ReactElement<any>, { nested: this.props.nested },
);
})
return childrenWithProps;
}
}
const NestedRoute: React.SFC<INestedRoute> = (props: INestedRoute) => {
return <Route path={`${props.nested}${props.path}`} component={props.component} />;
};
And routes with wrapper:
const MultiLanguage: React.SFC<RouteComponentProps<any>> = (props: RouteComponentProps<any>) => {
return (
<NestedRoutes nested={props.match.path} >
<NestedRoute path="/test" component={Login} />
<NestedRoute path="/no-match" component={NoMatch} />
</NestedRoutes>
);
};
export default (
<Switch>
<Route path="/:language" component={MultiLanguage}/>
<Route exact={true} path="/" component={Index} />
<Route path="/login" component={Login} />
<Route component={NoMatch} />
</Switch>
);
For nested routes there is a very simple way which i using.
Example main router is be like that
<Router history={history}>
<Switch >
<Route path="/" component={Home}></Route>
</Switch>
</Router>
Inside Home component using Nested Routing be like:
<div className="App">
<Navbar title="Home" links = { NavbarLinks }/>
{this.renderContentPage()}
</div>
renderContentPage will check the URL and render the nested route.
<Route exact path="/" component={Page1}></Route>
<Route exact path="/page1" component={Page1}></Route>
<Route exact path='/page2' component={Page2} />
So inside Home component page1 and page2 components rendered.
Route expects a single children i.e. a component.
It should not be a new Route.
What you can do is to include your nested routes inside your customers component.
Also make sure to remove exact inside the routes in customers component.