I've got react-redux private router not working.
It is just showing me a blank page of the route I am trying to access.
And my states are not loading at Redux Dev Tools.
Can it also be the problem of HashRouter?
My PrivateRoute Component:
import React from "react";
import { connect } from "react-redux";
import {Router, Redirect, withRouter } from "react-router-dom";
import PropTypes from 'prop-types';
class PrivateRoute extends React.Component {
static propTypes = {
isAuthenticated: PropTypes.bool,
}
isLoggedin = () => {
return this.props.isAuthenticated;
};
render = () => {
let { component: Component, ...rest } = this.props;
return (
<Router
{...rest}
render={(props) =>
this.isLoggedin() ? (
<Component {...props} />
) : props.location.pathname === "/" ? null : (
<Redirect to="/" />
)
}
/>
);
};
}
const mapStateToProps = (state) => ({
auth: state.auth,
isAuthenticated: state.auth.isAuthenticated
})
export default withRouter(connect(mapStateToProps, null)(PrivateRoute));
My App where I am wrapping my Routes to make it private:
import {Provider} from 'react-redux';
import store from './store'
import {loadUser} from './actions/authActions'
import PrivateRoute from './components/auth/PrivateRoute'
class App extends React.Component {
componentDidMount(){
store.dispatch(loadUser())
}
render(){
return (
<Provider store={store}>
<HashRouter basename="/">
<Navbar />
<Route exact path="/" component={Home}/>
<PrivateRoute path="/aeons" component={AeonsList} />
<PrivateRoute path="/carrefours" component={CarrefoursList} />
<PrivateRoute path="/farmers" component={FarmersList} />
<PrivateRoute path="/foodhalls" component={FoodhallsList} />
<PrivateRoute path="/grands" component={GrandsList} />
<PrivateRoute path="/heros" component={HerosList} />
<PrivateRoute path="/primos" component={PrimosList} />
<PrivateRoute path="/ranchos" component={RanchsList} />
</HashRouter>
</Provider>
)
}
}
export default App;
You need to import Route, not Router when defining your privateRoute component.
import {Route } from "react-router-dom";
Check this
Related
After adding PublicRoute and PrivateRoute my app not working popup login and signup. It displaying a blank screen. Before adding PublicRoute and PrivateRoute, my app working fine with Route.
The App has an initial home page when I click on the Merchant button it should popup for the login page. Please find the below screenshot.
But it displays with a blank screen. Please find below screenshot
Please help me to fix this issue.
AppRouter.js
import React from 'react';
import {Router, Route , Switch} from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import { HomePage } from '../components/HomePage';
import DashboardPage from '../components/DashboardPage';
import StartLogin from '../components/StartLogin';
import { UserForm } from '../components/UserForm';
import PublicRoute from './PublicRouter';
import PrivateRoute from './PrivateRouter';
export const history = createHistory()
const AppRouter = () => (
<Router history={history}>
<div>
<Switch>
<PublicRoute path="/" component={HomePage} exact={true}/>
<PrivateRoute path="/dashboard" component={DashboardPage} />
</Switch>
</div>
</Router>
);
export default AppRouter;
PublicRoute.js
import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
export const PublicRoute = ( {
isAuthenticated,
component: Component,
...rest}) => (
<Route {...rest} component={(props) => (
isAuthenticated ? (
<div>
<Redirect to='/dashboard' />
</div>
) : (
<Component {...props} />
)
)}/>
);
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.merchLogin
})
export default connect(mapStateToProps)(PublicRoute);
PrivateRoute.js
import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
import { HomePage } from '../components/HomePage';
export const PrivateRoute = ( {
isAuthenticated,
component: Component,
...rest}) => (
<Route {...rest} component={(props) => (
isAuthenticated ? (
<div>
<HomePage />
<Component {...props} />
</div>
) : (
<Redirect to='/' />
)
)}/>
);
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.merchLogin
})
export default connect(mapStateToProps)(PrivateRoute);
Remove isAuthenticated from PublicRoute.js
<Route path="/" component={HomePage} exact={true}/>
I'm attempting to create a private route based on the answers here, however it is not performing the redirect.
This is my private route component
import React, { Component, useContext } from "react";
import { Redirect, Route, RouteComponentProps, RouteProps } from "react-router-dom";
import { RootStoreContext } from "../stores/rootStore";
import { observer } from "mobx-react-lite";
const PrivateRoute: React.FC<RouteProps> = ({ children, ...rest }) => {
const rootStore = useContext(RootStoreContext);
const { isAuthenticated } = rootStore.msalStore;
return (
<Route
{...rest}
render={(props: RouteComponentProps<{}>) =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: "/", state: { from: props.location } }} />
)
}
/>
);
};
export default observer(PrivateRoute);
And this is my App.tsx
//Imports
const App: React.FC = () => {
return (
<>
<BrowserRouter>
<ThemeProvider theme={theme}>
<Route exact path="/" component={LoginPage} />
<Route path="/authcallback" component={AuthCallback} />
<PrivateRoute path="/home" component={HomePage} />
</ThemeProvider>
</BrowserRouter>
</>
);
};
export default App;
When I type /home in the browser, or create a test button that navigates to home, it loads the component even though isAuthenticated = false.
I know it's not related to the value of isAuthenticated in my mobx store as changing to const [isAuthenticated, setIsAuthenticated] = useState(false); doesn't work either. I also have no other instances of routing in my app.
What could be the issue here?
use Switch (from react-router-dom) to wrap routes,
import { Switch } from "react-router-dom";
const App: React.FC = () => {
return (
<>
<BrowserRouter>
<ThemeProvider theme={theme}>
<Switch>
<Route exact path="/" component={LoginPage} />
<Route path="/authcallback" component={AuthCallback} />
<PrivateRoute path="/home" component={HomePage} />
</Switch>
</ThemeProvider>
</BrowserRouter>
</>
);
};
export default App;
This discussion helped me
My solution...
https://codesandbox.io/s/modest-fire-6mj3i?file=/src/PrivateRoute.tsx:0-517
import * as React from "react";
import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import "./styles.css";
const Home: React.FC = () => {
return <div>Home</div>;
};
const Login: React.FC = () => {
return <div>Login</div>;
};
export default function App() {
return (
<BrowserRouter>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="/">Login</Link>
</li>
</ul>
<Switch>
<Route exact path="/" component={Login} />
<PrivateRoute redirectPath="/login" path="/home" component={Home} />
</Switch>
</BrowserRouter>
);
}
import { Redirect, Route, RouteProps } from "react-router";
import React, { useState } from "react";
export interface IPrivateRouteProps extends RouteProps {
redirectPath: string;
}
const PrivateRoute: React.FC<IPrivateRouteProps> = (props) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return isAuthenticated ? (
<Route {...props} component={props.component} render={undefined} />
) : (
<Redirect to={{ pathname: props.redirectPath }} />
);
};
export default PrivateRoute;
If I click Home I get redirected to /login and the Home component is hidden
useState is only used for testing the authenticated state from within the component.
I am trying to make a PrivateRoute component for react. Here is my higher order component. Can you tell me what is the problem with this.
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
export default ({ component: Component, ...rest }) => {
class PrivateRoute extends React.Component {
render() {
console.log("This is private route called");
if (this.props.profile) {
return (
<Route
{...rest}
render={props =>
this.props.profile.loggedIn === true ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);
}
}
}
const mapStateToProps = state => ({
profile: state.profile
});
return connect(mapStateToProps)(PrivateRoute);
};
Here's how you can accomplish a protected route via a protected route component.
Working example: https://codesandbox.io/s/yqo75n896x
containers/RequireAuth.js
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import ShowPlayerRoster from "../components/ShowPlayerRoster";
import ShowPlayerStats from "../components/ShowPlayerStats";
import Schedule from "../components/Schedule";
const RequireAuth = ({ match: { path }, isAuthenticated }) =>
!isAuthenticated ? (
<Redirect to="/signin" />
) : (
<div>
<Route exact path={`${path}/roster`} component={ShowPlayerRoster} />
<Route path={`${path}/roster/:id`} component={ShowPlayerStats} />
<Route path={`${path}/schedule`} component={Schedule} />
</div>
);
export default connect(state => ({
isAuthenticated: state.auth.isAuthenticated
}))(RequireAuth);
routes/index.js
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import Home from "../components/Home";
import Header from "../containers/Header";
import Info from "../components/Info";
import Sponsors from "../components/Sponsors";
import Signin from "../containers/Signin";
import RequireAuth from "../containers/RequireAuth";
import rootReducer from "../reducers";
const store = createStore(rootReducer);
export default () => (
<Provider store={store}>
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/info" component={Info} />
<Route path="/sponsors" component={Sponsors} />
<Route path="/protected" component={RequireAuth} />
<Route path="/signin" component={Signin} />
</Switch>
</div>
</BrowserRouter>
</Provider>
);
Or, if you want something that wraps all routes (instead of having to specify a protected route component). Then you can do something like the below.
Working example: https://codesandbox.io/s/5m2690nn6n
components/RequireAuth.js
import React, { Component, Fragment } from "react";
import { withRouter } from "react-router-dom";
import Login from "./Login";
import Header from "./Header";
class RequireAuth extends Component {
state = { isAuthenticated: false };
componentDidMount = () => {
if (!this.state.isAuthenticated) {
this.props.history.push("/");
}
};
componentDidUpdate = (prevProps, prevState) => {
if (
this.props.location.pathname !== prevProps.location.pathname &&
!this.state.isAuthenticated
) {
this.props.history.push("/");
}
};
isAuthed = () => this.setState({ isAuthenticated: true });
unAuth = () => this.setState({ isAuthenticated: false });
render = () =>
!this.state.isAuthenticated ? (
<Login isAuthed={this.isAuthed} />
) : (
<Fragment>
<Header unAuth={this.unAuth} />
{this.props.children}
</Fragment>
);
}
export default withRouter(RequireAuth);
routes/index.js
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "../components/Home";
import Players from "../components/Players";
import Schedule from "../components/Schedule";
import RequireAuth from "../components/RequireAuth";
export default () => (
<BrowserRouter>
<RequireAuth>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/players" component={Players} />
<Route path="/schedule" component={Schedule} />
</Switch>
</RequireAuth>
</BrowserRouter>
);
Or, if you want something a bit more modular, where you can pick and choose any route, then you can create a wrapper HOC. See this example (while it's written for v3 and not for authentication, it's still the same concept).
It looks like your render function's only return is inside of an if block, so its returning null. You need to fix the logic to just return the Route and make profile a required prop in your proptypes check instead of using an if block.
PrivateRoute.propTypes = {
profile: PropTypes.object.isRequired
};
I'm working on building a simple hacker news app using React and Redux and I'm having trouble with rendering an individual article page component (the ArticleSingle components).
The routing works fine, but the ArticleSingle component does not render on click.
Here's my Root file, which handles all of the routing:
// REACT
import React from 'react';
import PropTypes from 'prop-types';
// REACT ROUTER
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
// REDUX
import { Provider } from 'react-redux';
import createBrowserHistory from 'history/createBrowserHistory';
const history = createBrowserHistory();
// COMPONENTS
import AppContainer from './components/containers/AppContainer';
import Home from './components/presentational/Home';
import ArticleSingle from './components/presentational/ArticleSingle';
const Root = ({ store }) => (
<Provider store={store}>
<Router history={history}>
<Switch>
<Route path="/" component={AppContainer} />
<Route path="/" component={Home} />
<Route path="/topics/:topic_name/articles" component={Home} />
<Route path="/articles/:article_id" component={ArticleSingle} />
</Switch>
</Router>
</Provider>
);
Root.propTypes = {
store: PropTypes.object.isRequired
};
export default Root;
Here's the ArticleSingle component, which is the one that I'm having trouble with. I ultimately want to get it to pass the id from the URL to an action passed down from AppContainer, but I was hoping to get the component to render something on the page (the id) before starting the redux work.
import React from 'react';
import PropTypes from 'prop-types';
const ArticleSingle = ({ params, children }) => (
<div>
{params.article_id}
{children}
</div>
);
export default ArticleSingle;
Here's the Home component, it features a filter bar, what why there are two routes for it
import React from 'react';
import PropTypes from 'prop-types';
// Components
import Spinner from 'react-spinkit';
import ArticleList from './ArticleList';
import TopicsSubNav from './TopicsSubNav';
const Home = ({
loading,
articles,
topics,
fetchTopicArticles,
fetchArticles,
children
}) => {
return (
<div>
<h3 className="title is-3">Northcoders News</h3>
<div id="TopicsSubNav">
{loading && <Spinner name="pacman" color="coral" fadeIn="none" />}
<TopicsSubNav
topics={topics}
onTopicClick={fetchTopicArticles}
onAllClick={fetchArticles}
/>
</div>
{loading && <Spinner name="pacman" color="coral" fadeIn="none" />}
<ArticleList articles={articles} topics={topics} />
{ children }
</div>
);
};
Home.propTypes = {
articles: PropTypes.array.isRequired,
topics: PropTypes.array.isRequired,
loading: PropTypes.bool,
fetchTopicArticles: PropTypes.func.isRequired,
fetchArticles: PropTypes.func.isRequired
};
export default Home;
Here's the AppContainer, controls the state for all of the app.
import React from 'react';
import PropTypes from 'prop-types';
import Home from '../presentational/Home';
import ArticleSingle from '../presentational/ArticleSingle';
// Redux
import { connect } from 'react-redux';
import * as actions from '../../actions/actions';
class AppContainer extends React.Component {
componentDidMount () {
this.props.fetchArticles();
this.props.fetchTopics();
}
render () {
return (
<div>
<Home
articles={this.props.articles}
topics={this.props.topics}
loading={this.props.loading}
fetchTopicArticles={this.props.fetchTopicArticles}
fetchArticles={this.props.fetchArticles}
/>
</div>
);
}
}
function mapDispatchToProps (dispatch) {
return {
fetchArticles: () => {
dispatch(actions.fetchArticles());
},
fetchTopics: () => {
dispatch(actions.fetchTopics());
},
fetchTopicArticles: topic => {
dispatch(actions.fetchTopicArticles(topic));
},
fetchArticleById: id => {
dispatch(actions.fetchArticleById(id));
}
};
}
function mapStateToProps (state) {
return {
articles: state.articles.data,
article: state.article,
topics: state.topics.data,
loading: state.loading
};
}
AppContainer.propTypes = {
articles: PropTypes.array.isRequired,
topics: PropTypes.array.isRequired,
loading: PropTypes.bool,
fetchArticles: PropTypes.func.isRequired,
fetchTopics: PropTypes.func.isRequired,
fetchTopicArticles: PropTypes.func.isRequired,
fetchArticleById: PropTypes.func.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(AppContainer);
I see a couple of issues in your code.
1) Lets say you have <Route path="/articles/:article_id" component={ArticleSingle} /> - To access the article_id in ArticleSingle Componenet you need to use props.match.params.article_id
2) This is your route:
<Route path="/" component={AppContainer} />
<Route path="/" component={Home} />
<Route path="/topics/:topic_name/articles" component={Home} />
<Route path="/articles/:article_id" component={ArticleSingle} />
Basically when you visit /articles/article-id you are matching the first route <Route path="/" component={AppContainer} /> and you are getting into it.
To solve this issue you can do one of the following:
A. use exact in your first two routes. That would be <Route exact path="/" component={AppContainer} /> and <Route exact path="/" component={Home} />
B. Change the order of your routes and but the homepage root at the bottom like the following:
<Route path="/topics/:topic_name/articles" component={Home} />
<Route path="/articles/:article_id" component={ArticleSingle} />
<Route path="/" component={AppContainer} />
<Route path="/" component={Home} />
For the above solutions, I am assuming you are using React Router 4. I got the information I provided you from this tutorial:
https://medium.com/#pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf
I try to migrate to the react-router v4, where should I start? For most paths I need authentication and redicert. I installed last react-router-dom#next and react-router#next. Now when user is not login redirect to login page,
also I have login-callback, but have an error
WebSocket connection to 'ws://localhost:9998/sockjs-node/472/gnanejj3/websocket' failed: WebSocket is closed before the connection is established.
application.js
import React, {Component} from 'react'
import {connect} from 'react-redux'
import {IntlProvider} from 'react-intl'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import getMuiTheme from 'material-ui/styles/getMuiTheme'
import theme from './theme'
import * as AuthActions from './actions/auth'
import Routes from './routes'
export class Application extends Component {
static propTypes = {
locale: React.PropTypes.string.isRequired,
messages: React.PropTypes.objectOf(React.PropTypes.string.isRequired).isRequired,
login: React.PropTypes.func.isRequired,
redirectToLoginPage: React.PropTypes.func.isRequired,
isLoggedIn: React.PropTypes.bool.isRequired,
};
render = () =>
<IntlProvider locale={this.props.locale} messages={this.props.messages}>
<MuiThemeProvider muiTheme={getMuiTheme(theme)}>
<Routes />
</MuiThemeProvider>
</IntlProvider>;
}
export default Application
routes.js:
import React from 'react'
import {connect} from 'react-redux'
import {BrowserRouter, Route, Link ,Redirect, withRouter, Switch} from 'react-router-dom'
import Root from './components/root'
import Home from './components/homePage'
import Devices from './components/devices'
import Users from './components/users'
import AddOrEditUser from './components/users/addOrEdit'
import Plans from './components/plans'
import ServerSettings from './components/settings'
import About from './components/aboutPage'
import Patients from './components/patients'
import AddOrEditPatient from './components/patients/addOrEdit'
import Commissioning from './components/commissioning'
import * as AuthActions from './actions/auth'
import redirectToLoginPage from './api/auth'
const Routes = ({isLoggedIn, redirectToLoginPage, login}) =>
<BrowserRouter>
<Switch>
<Route path='/' render={({location}) => {
if (!isLoggedIn)
redirectToLoginPage()
return (
<Root>
<Switch>
<Route path='/home' component={Home} />
<Route path='/devices' component={Devices} />
<Route path='/users' component={Users} />
<Route path='/users/add' component={AddOrEditUser}/>
<Route path='/users/edit/:personId' component={AddOrEditUser}/>
<Patients path='/patients' render={() =>
<Switch>
<Route path='/add' component={AddOrEditPatient} />
<Route path='/edit/:personId' component={AddOrEditPatient} />
</Switch>
}
/>
<Route path='/plans' component={Plans} />
<Route path='/commissioning' component={Commissioning} />
<Route path='/server-settings' component={ServerSettings} />
<Route path='/about' component={About} />
</Switch>
</Root>)
}}
/>
<Route path='/login-callback' render={() => {
if (isLoggedIn)
login()
return <Root />
}}
/>
</Switch>
</BrowserRouter>
Routes.propTypes = {
// authenticate: React.PropTypes.func.isRequired,
// processLoginCallback: React.PropTypes.func.isRequired,
login: React.PropTypes.func.isRequired,
redirectToLoginPage: React.PropTypes.func.isRequired,
isLoggedIn: React.PropTypes.bool.isRequired,
}
export const mapDispatchToProps = dispatch => ({
login: () => dispatch(AuthActions.login()),
redirectToLoginPage: () => dispatch(AuthActions.redirectToLoginPage()),
})
export const mapStateToProps = state => ({
isLoggedIn: state.auth.isLoggedIn,
locale: state.intl.locale,
messages: state.intl.messages,
})
export default connect(mapStateToProps, mapDispatchToProps)(Routes)
That's a complex question for which a StackOverflow response box is too limiting. I've written a lengthy migration guide. If you're comfortable with react-router v2:v3, this will be a good resource for your migration:
https://medium.com/#francoisz/react-router-v4-unofficial-migration-guide-5a370b8905a
Since v4 does not support nested route, this code
<Route path='/' render={({location}) => {
if (!this.props.isLoggedIn)
this.props.redirectToLoginPage()
return ( ...
will probably stop working
I get around by creating some wrappers for the components .e.g.
const enforceLoginValidation = (children) => {
const fn = ({ match }) => {
if (!this.props.isLoggedIn) {
this.props.redirectToLoginPage()
return
}
// to pass react-router's match to the children
const extendedChild = React.cloneElement(children, { match });
return extendedChild;
};
return fn;
};
...
const HomePage = enforceLoginValidation(
<Home />
);
....
<Route path='/home' component={HomePage} />