I'm a new to React.js with asp.net core, and I got an error that I don't understand.
I created protected route like that :
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import auth from './auth.js'
export const ProtectedRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render = {props => {
if(auth.estAuthentifier()){
return <Component {...props}/>
} else {
return <Redirect to={
{
pathname: "/",
state: {
from: props.location
}
}
} />
}
}}
/>
);
}
In my App.js, I added the protected route like that :
import React, { Component } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router';
import { Login } from './components/Login'
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { ProtectedRoute } from './components/protected.route';
export default class App extends Component {
static displayName = App.name;
render () {
return (
<div>
<Switch>
<Route exact path="/" component={Login} />
<Layout>
<ProtectedRoute exact path="/home" component={Home}/>
</Layout>
<Route path="*" component={() => "404 NOT FOUND"}/>
</Switch>
</div>
);
}
}
And, the Layout component render all components with my NavBar included :
import React, { Component } from 'react';
import { Container } from 'reactstrap';
import { NavMenu } from './NavMenu';
import '../css/Layout.css';
export class Layout extends Component {
static displayName = Layout.name;
render () {
return (
<div className="conteneur-principal">
<NavMenu />
<Container>
{this.props.children}
</Container>
</div>
);
}
}
The problem is the next : I put the logout button in my navigation bar component but when I'm trying to logout and redirect to Login, i got the following error : TypeError: Cannot read property 'push' of undefined
NavMenu.js :
import React, { Component } from 'react';
import { NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
import auth from './auth';
import { withRouter } from 'react-router-dom';
import '../css/SideBarNav.css';
export class NavMenu extends Component {
static displayName = NavMenu.name;
constructor(props) {
super(props);
console.log(this.props);
this.deconnexion = this.deconnexion.bind(this);
}
deconnexion = () => {
auth.logout(() => {
this.props.history.push('/'); //LINE IN ERROR
})
}
render () {
return (
<div className="conteneur-menu">
<img src="images/familieu-logo-clear.png" alt="logo-familieu" title="logo-familieu" className="is-clickable logo-familieu"/>
<div className="conteneur-link">
<NavLink tag={Link} className="menu-link" to="/">Home</NavLink>
</div>
<input type="button" value="Déconnexion" className="btn-deconnexion" onClick={this.deconnexion}/>
</div>
);
}
}
What should I do to make my logout button work ?
Thanks alot
In NavMenu.js you are importing the withRouter, but is not used.
Wrap the export of component with withRouter like this:
export default withRouter(NavMenu);
Related
I'm testing some function, I discover a solution but I'm not sure that is correct way.
I need to load data from API so I used axios, and I need to share data between some child routes, I am currently using simple setState to keep values and passed them to the Route Components to be used.
Here is the MainContainer component
import React from 'react';
import axios from 'axios';
import { BrowserRouter, Route, Switch, Link, HashRouter, useLocation } from 'react-router-dom';
import Languages from './pages/Languages';
import Home from './pages/Home';
export default class MainContainer extends React.Component
{
constructor(props) {
super(props);
this.state = {
languages: null,
language:null,
}
}
get_init(language)
{
console.log("get_init")
var params = {
language:language,
}
axios
.get("https://xxxx.com/wp/it/api/init/")
.then(function(result) {
this.setState({languages:result.data.languages});
}.bind(this));
}
render() {
console.log("render")
return (
<div>
<Switch>
<Route path="/" component={() => <Languages get_init={this.get_init.bind(this)} languages={this.state.languages} />} exact />
<Route path="/:language/" component={(urlparam) => <Home language={urlparam.match.params.language} get_init={this.get_init.bind(this)} languages={this.state.languages} />} exact/>
<Route component={Error}/>
</Switch>
</div>
);
}
}
My Language Component is bellow
import axios from 'axios';
import React from 'react';
import LoaderData from './../helpers/LoaderData';
import { Link } from 'react-router-dom';
export default class Languages extends React.Component
{
constructor(props)
{
super(props);
if (props.languages==null)
{
this.props.get_init();
}
}
render() {
if (this.props.languages==null)
{
return '';
}
return (
<div>
<div className='main'>
LANGUAGE {Math.random()}<br/>
{
this.props.languages.map((language, i) =>
{
return (<div key={language.prefix}><Link to={language.prefix+"/"} >{language.name}</Link> <br/></div>)
})
}
</div>
</div>
);
}
}
is a correct way to build a website in react or there are better ways?
I have simple test project with App.js
import React, { Component, Suspense } from 'react';
import { BrowserRouter, Route, NavLink } from 'react-router-dom';
const Posts = React.lazy(() => import('./containers/Posts'));
class App extends Component {
render() {
return (
<BrowserRouter>
<React.Fragment>
<nav>
<NavLink to="/posts/222">Posts Page</NavLink>
</nav>
<Route path="/posts/:id" render={() => (
<Suspense fallback={<div>Loading...</div>}>
<Posts {...this.props} />
</Suspense>
)}
/>
</React.Fragment>
</BrowserRouter>
);
}
}
export default App;
and containers/Posts.js
import React, {Component} from 'react';
class Posts extends Component {
componentDidMount() {
console.log(this.props);
}
render() {
return (<h1>The Posts Page</h1>)
}
};
export default Posts;
and this.props in Posts is an empty object.
How can I get this.props.match.params.id in this case?
Wrap Posts with withRouter from react-router-dom
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class Posts extends Component {
componentDidMount() {
console.log(this.props);
}
render() {
return (<h1>The Posts Page</h1>)
}
};
export default withRouter(Posts);
I have a NavBar that's on multiple pages that allows me to search for an user. After the searching, clicking on it should re-direct me to the user page via Link from react-router-dom. The problem is this only works on the main page, and doesn't work when clicking from an user's page.
I've tried adding WithRouter to all my related components (containers, parents, and the children). Also tried fiddling with the Route pathing / exact pathing syntax to no avail.
*Removed components imports and non-relavant code for clarity.
/app.jsx
import React from 'react';
import { Provider } from 'react-redux';
import {
Route,
Redirect,
Switch,
Link,
HashRouter,
withRouter,
} from 'react-router-dom';
import { AuthRoute, ProtectedRoute } from '../utils/route_utils';
const App = () => (
<div className="main">
<HashRouter>
<Switch>
<AuthRoute exact path="/" component={LoginContainer} />
<AuthRoute exact path="/signup" component={SignupContainer} />
<ProtectedRoute exact path="/feed" component={FeedContainer} />
<ProtectedRoute exact path="/users/:userId" component={GreetingContainer} />
<ProtectedRoute exact path="/posts/:postId" component={PostShowContainer} />
<ProtectedRoute exact path="/upload" component={Upload} />
<ProtectedRoute exact path="/accounts/edit" component={AccountsEditContainer} />
<Route component={Errors} />
</Switch>
</HashRouter>
</div>
);
export default withRouter(App);
/greeting_container.jsx
import React from 'react';
import { withRouter } from 'react-router-dom';
const mapStateToProps = (state, ownProps) => {
...
};
const mapDispatchtoProps = (dispatch) => ({
....
});
export default withRouter(connect(mapStateToProps, mapDispatchtoProps)(Greeting));
/greeting.jsx
import React from 'react';
import { Link } from 'react-router-dom';
import NavBarContainer from '../nav_bar/nav_bar_container';
class Greeting extends React.Component {
constructor(props) {
super(props);
this.renderPosts = this.renderPosts.bind(this);
}
render() {
if (!this.props.user) return null;
const profileUser = parseInt(this.props.match.params.userId)
const user = this.props.user
return (
<div className="user-page-container">
<Modal currentUser={user} />
<NavBarContainer />
</div>
}
export default Greeting;
/nav_bar_container.jsx
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import NavBar from './nav_bar';
const mapStateToProps = (state) => {
....
};
const mapDispatchToProps = (dispatch) => ({
...
});
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(NavBar));
/nav_bar.jsx
import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import NavBarSearch from './nav_bar_search';
const NavBar = (props) => {
return (
<div>
< NavBarSearch
fetchSearchedUsers={props.fetchSearchedUsers}
searchResults={props.searchResults}
/>
</div>
);
};
export default withRouter(NavBar);
/nav_bar_search.jsx
import React from 'react';
import NavBarSearchResults from './nav_bar_search_results';
import { withRouter } from 'react-router-dom';
class NavBarSearch extends React.Component {
constructor(props) {
super(props);
this.state = {
searchInput: ''
}
this.generateSearch = this.generateSearch.bind(this)
}
render() {
return (
<div >
<input className="search-bar-test" onChange={this.generateSearch} type="text" placeholder='Search' />
{this.state.searchInput ? <NavBarSearchResults
searchResults={this.props.searchResults}
handleClick={this.handleClick}
// key={this.props.searchResults.id}
/> : null
}
</div>
)
}
}
export default withRouter(NavBarSearch);
/nav_bar_search_results.jsx
import React from 'react';
import { withRouter, Link } from 'react-router-dom';
const NavBarSearchResults = function({ searchResults, handleClick }) {
let results = searchResults ? Object.entries(searchResults).map((result) =>
<Link className="search-links" to={`/users/${result[0]}`} onClick={e => handleClick} key={result[0]}>
<div className="search-results-item">
<img className="search-results-image" src={result[1].photoUrl} />
{result[1].username}
</div>
</Link>
) : null
return (
<div className="search-results-container">
{results}
</div>
);
}
export default withRouter(NavBarSearchResults);
Once again, it is the Link in the Nav_Bar_Search_Results.jsx file that is not working. This file is nested several levels deep starting from the Greeting.jsx file. The string interpolation I'm doing is the user_id number. Please recall that the url parameters updates and the page works if I CLICK and REFRESH page, but not on first click alone.
The correct user page should render as specified by the url parameters
I am working with react-router and react-md. My problem is that when I change url through links the components not renders. If I press F5 works. My code is this:
Verions
"react-router": "^5.0.0",
"react-router-dom": "^5.0.0"
App.js
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
sidebarVisible: false
};
}
render() {
const { sidebarVisible } = this.state;
return (
<div>
<Header showDrawer={this.showDrawer} />
<Sidebar handleVisibility={this.handleVisibility} closeDrawer={this.closeDrawer} sidebarVisible={sidebarVisible} />
<Content />
</div>
);
}
}
export default App;
index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
WebFontLoader.load({
google: {
families: ['Roboto:300,400,500,700', 'Material Icons'],
},
});
render(
<Router>
<App />
</Router>
, document.getElementById('root'));
serviceWorker.unregister();
content.js
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class Content extends Component {
render() {
const { title } = this.props;
return (
<Router>
<div>
<Route exact path="/client" component={Client} />
<Route path="/payment" component={Payment} />
<Route path="/invoice" component={Invoice} />
</div>
</Router>
);
}
}
export default Content
NavItemLink.js
import React from 'react';
import { Link, Route } from 'react-router-dom';
const NavItemLink = ({ label, to, icon, exact }) => (
<Route path={to} exact={exact}>
{({ match }) => {
let leftIcon;
if (icon) {
leftIcon = <FontIcon>{icon}</FontIcon>;
}
return (
<ListItem
component={Link}
active={!!match}
to={to}
primaryText={label}
leftIcon={leftIcon}
/>
);
}}
</Route>
);
export default NavItemLink;
I think that in the content.js, react-router must detect the change and to renders the components.
I'm making a react app, I made a navbar and It renders in all of the components and I only want it visible in one, I made a HOC function but It still doesnt work correctly.
Higher Order Components
this is my navigation component
import React from 'react';
import {NavLink} from "react-router-dom";
const Navigation = () => {
return (
<div id = "navlinks">
<NavLink to = "">PROMOS</NavLink>
<NavLink to = "" >Nueva Orden</NavLink>
<NavLink to = "" >Ordenes</NavLink>
<NavLink to = "">Menú</NavLink>
<NavLink id = "logout" to = "/" >Cerrar Sesión</NavLink>
</div>
)
}
export default Navigation;
and this is is my router
import React, { Component } from 'react';
import { BrowserRouter , Route} from "react-router-dom";
import './App.css';
import Home from "./components/Home";
import Menu from "./components/Menu";
import Navigation from "./components/Navigation";
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Navigation/>
<div>
<Route path= "/" component = {Home} exact />
<Route path= "/Menu" component = {Menu}/>
</div>
</div>
</BrowserRouter>
);
}
}
export default App;
and my HOC component
import React, { Component } from 'react';
const LoaderHOC = (WrappedComponent) => {
return class LoaderHOC extends Component{
render(){
this.props.Navigation.length === 0 ? <div className = 'Loader'></div> : <WrapperComponent {... this.props}/>
}
}
}
export default LoaderHOC;
I suppose you have a way to determine whether your user is loggedIn or not. Suppose, you have store the information in isLoggedIn variable, than you can do following to hide navigation if user is not logged in,
{ isLoggedIn && <Navigation /> }
But once your application grows, I suggest you to make different routes depending on the public/private state.
Create a PrivateRoute.js file
import React, { Component } from 'react';
import { Redirect, Route } from 'react-router-dom';
import Navigation from "./components/Navigation";
class PrivateRoute extends Component {
render() {
// use your own logic here
const isLoggedIn = !!localStorage.getItem('token');
if (!isLoggedIn) {
return <Redirect to='/' />;
}
return (
<div>
<Navigation />
// your private route
</div>
}
}
export default PrivateRoute;
create your PublicRoute.js file
import React, { Component } from 'react';
import { Redirect, Route } from 'react-router-dom';
class PublicRoute extends Component {
render() {
<div>
// your all public route
</div>
}
}
export default PublicRoute;
Now Just include those into your main file
import React, { Component } from 'react';
import { BrowserRouter , Route} from "react-router-dom";
import { PublicRoute, PrivateRoute } from './routes';
import './App.css';
import Home from "./components/Home";
import Menu from "./components/Menu";
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<PublicRoute />
<PrivateRoute />
</div>
</BrowserRouter>
);
}
}
export default App;
Don't use HOC for this.
You must have store somewhere that user is loggedIn, if not I would suggest you to use a localStorage like,
localStorage.setItem("loggedIn", true);
Note: To setup a localStorage you don't need any extra configuration.
In your router you can use this to hide your component,
{localStorage.getItem("loggedIn") && <Navigation/>}