Hey guys i need to implement a button that will have a route assigned, i am writing on top of the existed code so I am a bit lost, there is a private route component wrapped in Switch component. How to i assign a route to my buttons onClick
<Switch>
<PrivateRoute exact path="/settings" component={Settings}/>
<PrivateRoute exact path="/admin" component={AdminButton}/>
The Private Route consist of the following
#inject('auth')
#observer
class PrivateRoute extends Component {
render() {
const {auth, component, ...rest} = this.props;
const RouteComponent = component;
const {authenticated, authenticating} = auth;
return (
<Route
{...rest}
render={props =>
(authenticated || authenticating) ? (
<RouteComponent {...props} />
) : (
<Redirect to="/login" />
)
}
/>
AdminButton component that needs to receive something to onClick i guess
export default class AdminButton extends React.Component {
render() {
return <QuestionButtonStyled onclick={}>Admin Panel</QuestionButtonStyled>
}
}
App.jsx
<DashboardLayout>
{this.props.auth.authenticated && <EpMenu/> }
<div style={{marginTop: '20px'}}>
<Switch>
<Route exact path="/login/:token?" component={LoginPage}/>
<PrivateRoute exact path="/welcome" component={Welcome}/>
<PrivateRoute exact path="/monthly-payslip" component={MonthlyPayslip}/>
<PrivateRoute exact path="/yearly-tax-overview" component={YearlyTaxStatementOverview}/>
<PrivateRoute exact path="/social-security-documents" component={SocialSecurityDocuments}/>
<PrivateRoute exact path="/central-information-documents" component={CentralDocuments}/>
<PrivateRoute exact path="/settings" component={Settings}/>
<PrivateRoute exact path="/admin" component={AdminButton}/>
<Redirect from="/*" to="/login"/>
</Switch>
</div>
</DashboardLayout>
Since you want to change the route on onClick event, you can make use of history from props and pass on the new path like
export default class AdminButton extends React.Component {
handleClick=() => {
this.props.history.push('/settings');
}
render() {
return <QuestionButtonStyled onClick={this.handleClick}>Admin Panel</QuestionButtonStyled>
}
}
Related
I am trying to build a full stack application with User login/logout functionality.
I want to protect certain pages such that they can only be viewed when the user is logged in. For login I have created a REST API and I am using session storage to keep track of whether the user is logged in or not.
validateUser = () => {
let user = {
username: this.state.email,
password: this.state.password,
//status: "LOGGED_IN"
};
UserService.authenticateUser(user).then((res) => {
if(res.data === 'SUCCESS') {
window.sessionStorage.setItem("isUserLogged", true);
} else if(res.data === 'FAILURE') {
window.sessionStorage.setItem("isUserLogged", false);
this.resetLoginForm();
this.setState({"error":"Invalid username or password"});
}
})
};
Tis is my App.js
function App() {
return (
<div>
<Router>
<HeaderComponent/>
<div className="container">
<Switch>
<Route path="/" exact component={LandingPageComponent}></Route>
{/* <Route path ="/customers" component = {ListCustomerComponent}></Route> */}
{/* <Route path ="/add-customer/:id" component = {CreateCustomerComponent}></Route> */}
<Route path = "/view-customer/:id" component = {ViewCustomerComponent}></Route>
<Route path = "/admin-login" component = {AdminLoginComponent}></Route>
<Route path = "/admin-register" component = {AdminResgisterComponent}></Route>
<Route path="/customers" exact render={() => (
window.sessionStorage.getItem("isUserLogged") === "true"
? <ListCustomerComponent />
: <Redirect to='/admin-login' />
)} />
<Route path="/add-customer/:id" exact render={() => (
window.sessionStorage.getItem("isUserLogged") === "true"
? <CreateCustomerComponent />
: <Redirect to='/admin-login' />
)} />
</Switch>
</div>
<FooterComponent/>
</Router>
</div>
);
}
export default App;
Everything works fine if I don't check my session storage. But when I try to implement the conditional routes as shown above I start getting errors.
If I just put simple routes, then I don't encounter this error.
Any help would be highly appreciated.
You didn't pass Route props into your component. So history does not included in props, you can console.log(this.props) to check what this.props contains.
To fix it, let's pass Route props into your components
<Route path="/add-customer/:id" exact render={(props) => (
window.sessionStorage.getItem("isUserLogged") === "true"
? <CreateCustomerComponent {...props} /> // ADD PROPS HERE
: <Redirect to='/admin-login' />
} />
You didn't show what you did on ListCustomerComponent.
You could try to encapsulate your component using HOC withRouter or if you are using Functional component, use useHistory hook.
// on export class component
export default withRouter(YourComponent)
in functional component, you can use
const YourComponent = ()=>{
const history = useHistory();
// then you can say something such as
// history.push(...)
return <>...your view here...</>
}
export default YourComponent;
<Switch>
{/* Login Sections goes Here */}
<Route exact path='/' component={MainPage} />
<Route exact path='/login' component={Login} />
<Route exact path='/admin/' component={LoginAdmin} />
<Route exact path='/register' component={Register} />
{/* AdminUser ROutes goes here */}
<SuperUserDashboard>
<Route exact path='/admin/dashboard' component={Dashboardpage} />
<Route exact path='/admin/users' component={UsersAdmin} />
</SuperUserDashboard>
<Route exact path='' component={Notfound} />
</Switch>
in superuser dashboard check if user is authenticated if not redirect to admin login page else all the routes will be visible
Actually I had no problem with directing to another route by clicking a button, but somehow I can't direct manually by changing the URL. Every time I was about changing the URL (ex: localhost:3000/proposal), it always directs me to the first Route. Here's the Route in order :
<Switch>
<Route exact path="/" component={Landing} /> // => always goin here
<Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} />
{/* Dashboard */}
<PrivateRoute
exact
path="/home"
component={Home}
StickyNav={StickyNavbar}
/>
<PrivateRoute
exact
path="/proposal"
component={Proposal}
StickyNav={StickyNavbar}
/>
<PrivateRoute
exact
path="/laporan"
component={Laporan}
StickyNav={StickyNavbar}
/>
<Route component={NotFound} />
</Switch>
It doesn't direct me to Landing if I change the URL to non-private route. Here's my private route code :
import React from "react"
import { Route, Redirect } from "react-router-dom"
import { connect } from "react-redux"
import PropTypes from "prop-types"
const mapStateToProps = state => ({
auth: state.auth
})
const PrivateRoute = ({ component: Component, auth, ...rest }) => (
<Route
{...rest}
render={props =>
auth.isAuthenticated === true ? (
<Component {...props} {...rest} />
) : (
<Redirect to="/login" />
)
}
/>
)
PrivateRoute.propTypes = {
auth: PropTypes.object.isRequired
}
export default connect(mapStateToProps)(PrivateRoute)
Based on #zhuber said, the auth object from react-redux doesn't call before the private route was called. So I changed the condition from isAuthenticated using localStorage like this :
!isEmpty(localStorage.jwtToken) ? (
<Component {...props} {...rest} />
) : (
<Redirect to="/login" />
)
I have a simple React app and I'm trying to implement auth logic. This is how my application looks like:
class App extends Component {
render() {
return (
<Router history={history}>
<div className="App">
<Switch>
<PrivateRoute path="/account" component={Account} />
<Route component={PublicLayout} />
</Switch>
</div>
</Router>
)
}
}
My auth logic is: if user isAuthenticated === true render /account page else redirect to /signin. So for this I created my own simple PrivateRoute functional component:
export default ({ component: Component, ...args }) => {
return (
<div>
<Route {...args} render={(props) => {
return fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to='/signin' />
}} />
</div>
)
}
Because PrivateRoute path="/account" is NOT exact path I expect that if user goes to /account-setting it will redirect him to /sign-in. But it's not working. The user successfully goes to /account-setting and see PublicLayout.
Why is that? There is no exact key in route, it has to grab all that starts from "/account" and use my logic in the PrivateRoute functional component.
Not providing exact props doesn't mean that it will match /account, /account-settings but it will match /account, /account/other, /account/other/paths. So, you'll need to provide both path:
<PrivateRoute path="/account" component={Account} />
<PrivateRoute path="/account-setting" component={Account} />
Or, you may use regex:
<PrivateRoute path="/account(?=\S*)([a-zA-Z-]+)" component={Account} />
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.
I am new to react-router and I just started writing an app using react-router V4. I would like to to pass props to components rendered by <Match /> and I am wondering about what's the 'best' or 'proper' way to do so.
Is it by doing something like this?
<Match pattern="/" render={
(defaultProps) => <MyComponent myProp = {myProp} {...defaultProps} />
}/>
Is this (passing props to components to be rendered by <Match />) even a good practice to do so with react-router or is it an antipattern or something; and if so, why?
You must use the render prop instead of component to pass on custom props, otherwise only default Route props are passed ({match, location, history}).
I pass my props to the Router and child components like so.
class App extends Component {
render() {
const {another} = this.props
return <Routes myVariable={2} myBool another={another}/>
}
}
const Routes = (props) =>
<Switch>
<Route path="/public" render={ (routeProps) =>
<Public routeProps={routeProps} {...props}/>
}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/" render={ (routeProps) =>
...
}/>
</Switch>
render() {
return (
<Router history={browserHistory}>
<Switch>
<Route path="/"
render={ () => <Header
title={"I am Title"}
status={"Here is my status"}
/> }
/>
<Route path="/audience" component={Audience}/>
<Route path="/speaker" component={Speaker}/>
</Switch>
</Router>
)
}
I'm fairly new to react-router and came across a similar issue. I've created a wrapper based on the Documentation and that seems to work.
// Wrap Component Routes
function RouteWrapper(props) {
const {component: Component, ...opts } = props
return (
<Route {...opts} render={props => (
<Component {...props} {...opts}/>
)}/>
)
}
<RouteWrapper path='/' exact loggedIn anotherValue='blah' component={MyComponent} />
So far so good
I use render in combination with a defined method like so:
class App extends React.Component {
childRoute (ChildComponent, match) {
return <ChildComponent {...this.props} {...match} />
}
render () {
<Match pattern='/' render={this.childRoute.bind(this, MyComponent)} />
}
}
The render prop is meant for writing inline matches, so your example is the ideal way to pass extra props.
I'll do it like the following to improve clarity.
const myComponent = <MyComponent myProp={myProp} {...defaultProps} />
<Match pattern="/" component={myComponent} />
By this your router code won't get messed up!.