I started with create-react-app and am attempting to set up auth-based routing via a Gatekeeper component. Right now the only thing the Gatekeeper component does is render its children. I use redux to get the current user from the state tree and save it as currentUser. I'll use that to validate access to the child routes later.
import React from 'react';
import { connect } from 'react-redux';
import { subscribeToCurrentUser } from '../../reducers/authentication';
class Gatekeeper extends React.Component {
componentDidMount() {
this.props.subscribeToCurrentUser();
}
render() {
return this.props.children
}
}
function mapStateToProps(state) {
return {
currentUser: state.currentUser
}
}
const GatekeeperContainer = connect(mapStateToProps, {subscribeToCurrentUser})(Gatekeeper);
export default GatekeeperContainer;
If I initially load the app on, say /account everything loads as expected. But if I navigate to /templates/123 via a <NavLink> the URL changes but the <Template> component doesn't render. Inspecting with React Dev Tools shows me the children of every route underneath the Gatekeeper component is null. If I refresh the page then the <Template> component renders as expected but navigating back to /account doesn't render the <AccountPage> component.
<Provider store={store}>
<Router history={history}>
<div>
<Route exact path="/" component={LandingPage} />
<Layout>
<Route path="/login" component={Login} />
<Gatekeeper>
<Switch>
<Route path="/home" component={HomePage} />
<Route path="/templates/:templateID" component={Template} />
<Route path="/account" component={AccountPage} />
</Switch>
</Gatekeeper>
</Layout>
</div>
</Router>
</Provider>
What am I doing wrong here?
Related
I try to put 2 HOC in switch but, only routers in first be called, the second is not called.
// if user is not login, show login page, otherwise add a side bar to children and show up
#inject("userStore")
#observer
class Auth extends React.Component {
render() {
let { userStore, children } = this.props;
return userStore.isLogin ? <CoreLayout>{children}</CoreLayout> : <Login />;
}
}
// if user is not login, show login page, otherwise show children
#inject("userStore")
#observer
class AuthWithoutLayout extends React.Component {
render() {
let { userStore, children } = this.props;
return userStore.isLogin ? children : <Login />;
}
}
export { Auth, AuthWithoutLayout };
And the Switch part:
<ConfigProvider locale={locale}>
<Switch>
<Route exact path="/" component={Login} />
<AuthWithoutLayout>
<Route path="/switch-role" component={SwitchRole} />
</AuthWithoutLayout>
<Auth>
<Route path="/user-list" component={UserList} />
</Auth>
</Switch>
</ConfigProvider>
If I input /localhost:3000/switch-role to browser, child page can show up correctly, but if I input /localhost:3000/user-list, I see a black page. if I remove AuthWithoutLayout part, the user-list page will show up.
Please help.
Switch
Renders the first child <Route> or <Redirect> that matches the location.
BTW, neither of those are Higher Order Components, but rather they are simple wrapper components. You can correct your Auth component, but your AuthWithoutLayout is a layout container and better suited to decorate anything other than a route or redirect.
Basically in your "auth" component you want to check some authentication condition and if authenticated render the Route, otherwise you redirect the user where you want them, usually the login path.
Your containers should also apply the Single Responsibility Principle, meaning an auth container should only concern itself with authentication, and a layout container should only concern itself with content layout.
Here's a sample auth route rewrite
// if user is logged in, render Route, otherwise Redirect to login "/"
#inject("userStore")
#observer
class AuthRoute extends Component {
render() {
const { userStore, ...props } = this.props;
return userStore.isLogin ? <Route {...props} : <Redirect to="/" />;
}
}
Usage:
<ConfigProvider locale={locale}>
<Switch>
<Route exact path="/" component={Login} />
<AuthRoute path="/switch-role" component={SwitchRole} />
<AuthRoute path="/user-list" component={UserList} /> // <-- use a layout container to decorate UserList!
</Switch>
</ConfigProvider>
The problem with above code is that Switch renders the First match component. So when you render AuthWithoutLayout without a Route, it assumes that this is the component that needs to be rendered and will not check any further and hence Auth is ignored
The solution is to write AuthWithoutLayout and Auth both with Routes
<ConfigProvider locale={locale}>
<Switch>
<Route exact path="/" component={Login} />
<Route path="/switch-role">
<AuthWithoutLayout>
<SwitchRole />
</AuthWithoutLayout>
</Route>
<Route path="/user-list">
<Auth>
<UserList />
</Auth>
</Route>
</Switch>
</ConfigProvider>
I'm starting in React and I'm curious about about if have any way to change a page without reload all the html, changing only a content component for example.
I know that there is a way to change the component without change the url but I thought that if the url change too the application would be better.
React Router is the exact thing you're looking for
Here, how you can achieve what you're looking for.
First, wrap your app with BrowserRouter
import { BrowserRouter } from "react-router-dom";
import React from 'react';
class App extends React.Component {
return (){
return (
<BrowserRouter>
<SomeComponent />
</BrowserRouter>
)
}
}
Now just use the Route and Link. Route told the application which component to render on the basis of the current route and Link changes the URL without reloading the whole page
import { Route, Link, Switch } from "react-router-dom";
import React from 'react';
import {Circle, Square} from './someFileWithComponents';
class SomeComponent extends React.Component {
render(){
return (
<div>
<Link to='/circle' >Circle</Link>
<Link to='/square' >Square</Link>
<Switch>
<Route path='/circle' component={Circle} />
<Route path='/square' component={Square} />
</Switch>
</div>
)
}
}
React Router is what you looking for
const AppRouter =()=>(
<BrowserRouter>
<div>
<Header/>//where Header components contains the navigation
<Switch>
<Route path="/" component={BookListPage} exact={true} />
<Route path="/create" component={AddBookItem} />
<Route path="/edit/:id" component={EditBookItem} />
<Route path="/help" component={HelpPage} />
<Route component={NotFoundPage} />
</Switch>
</div>
</BrowserRouter>
);
export default AppRouter;
I've been following this tutorial for creating a react project. The tutorial led to creating multiple react components that have a simple sample text within them. This would allow for the testing of the react-router-dom.
example of the simple component, all other components are similar.
import React, { Component } from 'react'
export default class Cart extends Component {
render() {
return (
<div>
<h3>Hello From Cart</h3>
</div>
)
}
}
The components are displayed using a react router which switches the displayed component depending on the url
class App extends React.Component {
render() {
return (
<React.Fragment>
<NavBar/>
<Switch>
<Route path="/details" Component={Details} />
<Route path="/cart" Component={Cart} />
<Route path="/" Component={ProductList} />
<Route Component={Default} />
</Switch>
</React.Fragment>
);
}
}
Furthermore to avoid confusion, my browser router is encapulating my App component from the index.js
ReactDOM.render(
<Router>
<App />
</Router>
, document.getElementById('root'));
When I navigate to the /cart url on my local host these are my results.
What should be displayed:
https://imgur.com/fZw5QnP.png
However, what is displayed is:
https://i.imgur.com/F1O07Y8.png
Please help me fix this issue, thank you.
I realized my error, I had "Component={}" within the Route, when it was supposed to be "component={}" (lowercase "c"). I'm posting this for all those, who have the same issue.
In the application the navigation appears single in all the pages except the home which is '/'.
How do I prevent the navigation from appearing twice in the home. Here is a screenshot and the code for the react-router.
Screen Shot OF Double Menu In React-Router:
Here is the code:
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Navigator />
<Switch>
<Route path='/'exact strict component ={HomeIndex} />
<Route path='/Pricing' exact component ={Pricing} />
<Route component={Error404}/>
</Switch>
</div>
</BrowserRouter>
);
}
}
Once check your HomeIndex component, may be you are using <Navigator /> again inside HomeIndex component.
I have some nested routes written in react router v4 and in the Navigation component I have an input box.
On submit I need to call a method on a different route (on Landing component) and pass the value. I can't find any example to call a method in Route.
Any other way/ workaround to use a navigation with data and different routes is welcome.
My routes:
return (
<div>
<Navigation callSearch = {this.callSearch.bind(this)} />
<Switch>
<Route path="/u/:slug" component={AuthorPage}/>
<Route path="/:location/:slug" component={PhotoDetails}/>
<Route path="/" component={Landing} />
</Switch>
</div>
)
In Navigation i call callSearch() :
searchPhotos(e) {
e.preventDefault();
if(this.state.searchTerm) {
this.props.callSearch(this.state.searchTerm);
}
}
I have fixed this issue in my application using withRouter from react-router module.
import { Route } from "react-router-dom";
import { withRouter } from "react-router";
class SideBar extends Component {
callSearch = (searchKeyword) => {
if(searchKeyword) this.props.history.push(`/u/${searchKeyword}`);
};
render() {
return (
<div>
<Navigation callSearch = {this.callSearch} />
<Switch>
<Route path="/u/:slug" component={AuthorPage}/>
<Route path="/:location/:slug" component={PhotoDetails}/>
<Route path="/" component={Landing} />
</Switch>
</div>
)
}
export default withRouter(SideBar);
Hope this will help anyone else !!!