I am developing a React app (with react-router-dom) and trying to call a function defined in the parent component App. The parent component has been defined like so
import React, { Component, Fragment } from 'react';
import { Link, NavLink } from 'react-router-dom';
import Routes from './components/routes';
import { withRouter } from 'react-router';
import { Auth } from "aws-amplify";
import Login from './components/Login';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.userHasAuthenticated = this.userHasAuthenticated.bind(this);
this.state = {
isAuthenticated: false
}
}
userHasAuthenticated = (value) => {
this.setState({ isAuthenticated: value });
}
handleLogout = async event => {
await Auth.signOut();
this.userHasAuthenticated(false);
this.props.history.push("/");
}
async componentDidMount() {
try {
await Auth.currentSession();
this.userHasAuthenticated(true);
this.props.history.push("/chat");
} catch(e) {
if (e !== 'No current user') {
alert(e);
}
}
}
render() {
return (
<Fragment>
<div className="navbar navbar-expand-lg navbar-light bg-light">
<Link to="/" className="navbar-brand" href="#"><h1>Sample App</h1></Link>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
{this.state.isAuthenticated ?
<Fragment>
<li className="nav-item">
<NavLink to="/chat" className="nav-link">Chat</NavLink>
</li>
<li className="nav-item">
<NavLink to="/" className="nav-link" onClick={this.handleLogout}>Logout</NavLink>
</li>
</Fragment> :
<Fragment>
<li className="nav-item">
<NavLink to="/" className="nav-link">Login</NavLink>
</li>
<li className="nav-item">
<NavLink to="/Signup" className="nav-link">Signup</NavLink>
</li>
</Fragment>
}
</ul>
</div>
</div>
<Routes userHasAuthenticated= { this.userHasAuthenticated } isAuthenticated = { this.state.isAuthenticated }/>
</Fragment>
);
}
}
export default withRouter(App);
The login component is supposed to authenticate the user, update the state (using the hasUserAuthenticated function defined in the parent component) and redirect the user to another page.
import React, { Component } from "react";
import { FormGroup, FormControl, FormLabel, Button } from "react-bootstrap";
import { Auth } from "aws-amplify";
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
}
validateForm() {
return this.state.email.length > 0 && this.state.password.length>0;
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
handleSubmit = async event => {
event.preventDefault();
try {
await Auth.signIn(this.state.email, this.state.password);
this.userHasAuthenticated(true);
this.props.history.push("/chat");
} catch (e) {
alert(e.message);
}
}
render() {
return (
<div className="Home">
<div className="col-md-4">
<form onSubmit={this.handleSubmit}>
<FormGroup controlId="email">
<FormLabel>Email</FormLabel>
<FormControl
autoFocus
type="email"
value={this.state.email}
onChange={this.handleChange}
/>
</FormGroup>
<FormGroup controlId="password" >
<FormLabel>Password</FormLabel>
<FormControl
value={this.state.password}
onChange={this.handleChange}
type="password"
/>
</FormGroup>
<Button type="submit">
Login
</Button>
</form>
</div>
</div>
);
}
}
My Routes component looks like below:
import React from 'react';
import Signup from './Signup';
import Login from './Login';
import NotFound from './NotFound';
import chat from './chat';
import { Route, Switch } from "react-router-dom";
export default ( { childProps } ) =>
<Switch>
<Route exact path ="/" component={Login} props={childProps}/>
<Route exact path ="/Signup" component={Signup} props={childProps}/>
<Route exact path ="/chat" component={chat} props={childProps}/>
<Route component={NotFound} />
</Switch>;
However, the app throws an error saying this.userHasAuthenticated is not a function. What am I doing wrong? Any help would be welcome.
In Routes.js you need these changes
import React from 'react';
import Signup from './Signup';
import Login from './Login';
import NotFound from './NotFound';
import Chat from './chat';
import { Route, Switch } from "react-router-dom";
export default parentProps =>
<Switch>
<Route exact path ="/" render={(props => <Login {...props} {...parentProps} />) />
<Route exact path ="/Signup" render={(props => <Signup {...props} {...parentProps} />) />
<Route exact path ="/chat" render={(props => <Chat {...props} {...parentProps} />) />
<Route component={NotFound} />
</Switch>;
And also in Login or any child component use this.props.userHasAuthenticated(true); instead of this.userHasAuthenticated(true);
Related
This question already has an answer here:
Difference in the navigation (React Router v6)
(1 answer)
Closed last month.
I am creating a website that is using React Router. All my links are working fine in the header but when I try to create a link on my home page it will not work. There must be a step I'm missing. Any help would be appreciated!
App:
import React, { useEffect } from "react";
import { useState } from 'react';
import { BrowserRouter, Routes, Route, Link } from "react-router-dom"
import Header from './Header';
import MilesForm from "./MilesForm";
import Weather from './Weather';
import PaceCalculator from "./PaceCalculator";
import WeeklyGoal from "./WeeklyGoal";
import Home from "./Home";
const getGoal = JSON.parse(localStorage.getItem('goal') || "[]");
const App = () => {
const [goal, setGoal] = useState(getGoal);
const [milesToGo, setMilesToGo] = useState();
const handleChange = (e) => {
setGoal(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault();
setMilesToGo(goal)
localStorage.setItem('goal', goal)
window.location.reload();
}
return (
<BrowserRouter>
<div>
<Header />
<Routes>
{/* <Route path="/" element={<Home />} /> */}
<Route path="Home" element={<Home />} />
<Route path="WeeklyGoal" element={[
<WeeklyGoal
handleChange={handleChange}
handleSubmit={handleSubmit}
goal={goal}
/>,
<MilesForm
goal={goal}
milesToGo={milesToGo}
setMilesToGo={setMilesToGo}
path="./MilesForm"
/>
]} />
<Route path="Weather" element={<Weather />} />
<Route path="Pace" element={<PaceCalculator />} />
</Routes>
</div>
</BrowserRouter>
);
};
export default App;
Header:
import React from "react";
import './css/header.css';
import { Link } from 'react-router-dom';
import { useState } from 'react';
const Header = () => {
const [burger, setBurger] = useState(true)
const handleToggle = () => {
{ burger ? setBurger(false) : setBurger(true) }
}
return (
<div className="header-style">
<h1 className="header-title">3-2run</h1>
<button className="burger-btn" onClick={handleToggle}>
<p></p>
<p></p>
<p></p>
</button>
<div
className={burger === false
? "menu-container"
: "hide-menu-container"
}
>
<ul>
<li><Link to="Home">Home</Link></li>
<li><Link to="WeeklyGoal">Track your miles</Link></li>
<li><Link to="Weather">Weather</Link></li>
<li><Link to="Pace">Pace Calculator</Link></li>
</ul>
</div>
</div>
)
}
export default Header;
Home: Here I have a link to the weather page that is not working. I'm confused as to why this same link will work on the header page but not on this one.
import React from 'react';
import './css/home.css';
import { Link } from 'react-router-dom'
const Home = () => {
return (
<div className='home-container'>
<div className='track-miles-link'>
<h2>Track Your Miles</h2>
</div>
<div className='get-weather'>
<Link to="Weather"><h2>Check Weather</h2></Link>
</div>
<div className='get-pace'>
<h2>Pace Calculator</h2>
</div>
</div>
)
}
export default Home;
You don't have a route with the path /Home/Weather so
<Link to="Weather"><h2>Check Weather</h2></Link>
won't work
You have /Weather route so
<Link to="/Weather"><h2>Check Weather</h2></Link>
or
<Link to="../Weather"><h2>Check Weather</h2></Link>
will work
Im using React 18, React Router 6 and React Auth Kit 2.7
I tried to do login page, as showed in example for RAK link
But i getting this error
Code for Login Component JSX
import React from "react"
import axios from "axios"
import { useIsAuthenticated, useSignIn } from "react-auth-kit"
import { useNavigate, Navigate } from "react-router-dom"
const SignInComponent = () => {
const isAuthenticated = useIsAuthenticated()
const signIn = useSignIn()
const navigate = useNavigate()
const [formData, setFormData] = React.useState({ login: "", password: "" })
async function onSubmit(e) {
e.preventDefault()
axios.post("http://localhost:3030/api/auth/login", formData).then((res) => {
if (res.status === 200) {
if (
signIn({
token: res.data.token,
expiresIn: res.data.expiresIn,
tokenType: "Bearer",
authState: res.data.authUserState,
})
) {
navigate("/profile")
console.log("logged in")
} else {
//Throw error
}
} else {
console.log("da duck you want")
}
})
}
console.log(isAuthenticated())
if (isAuthenticated()) {
// If authenticated user, then redirect to his profile
return <Navigate to={"/profile"} replace />
} else {
return (
<form onSubmit={onSubmit} className="flex flex-col w-96 p-2">
<input
className="text-black mt-2"
type={"login"}
onChange={(e) => setFormData({ ...formData, login: e.target.value })}
/>
<input
className="text-black mt-2"
type={"password"}
onChange={(e) =>
setFormData({ ...formData, password: e.target.value })
}
/>
<button type="submit">Submit</button>
</form>
)
}
}
export default SignInComponent
Routes.jsx
// system
import React from 'react'
import { RequireAuth } from 'react-auth-kit'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
// pages
import DeveloperPage from "../pages/Dev.page";
import MainPage from "../pages/Main.page";
import ProfilePage from "../pages/profile/Profile.page";
import LoginPage from "../pages/Auth/Login.page.auth"
import RegisterPage from "../pages/Auth/Register.page.auth"
// components
// logic
const RoutesComponent = () => {
return (
<BrowserRouter>
<Routes>
{/* main */}
<Route path={"/"} element={<MainPage />} />
{/* Authentication */}
<Route path={"/login"} element={<LoginPage />} />
<Route path={"/register"} element={<RegisterPage />} />
{/* Developer */}
<Route path={"/dev"} element={<DeveloperPage />} />
{/* Other */}
<Route path={"/profile"} element={
<RequireAuth loginPath={"/login"}>
<ProfilePage />
</RequireAuth>
} />
</Routes>
</BrowserRouter>
);
};
export default RoutesComponent;
Profile.jsx
import React, { useState } from 'react'
export default function App() {
return (
<div className="wrapper">
<h1>Profile</h1>
</div>
);
}
Im already tried searching this error all over stackoverflow, github issues and link that provided by error but still dont understand how to fix error in my example
Updated:
App.jsx
import React from "react";
import { AuthProvider } from "react-auth-kit";
import RoutesComponent from "./routes/router";
import "./index.css";
function App() {
return (
<AuthProvider authName={"_auth"} authType={"localstorage"}>
<RoutesComponent />
</AuthProvider>
);
}
export default App;
Have you put your application inside AuthProvider? https://github.com/react-auth-kit/react-auth-kit/blob/master/examples/create-react-app/src/App.js
I am a beginner in ReactJS and React Router and I am having some issues with my nested router.
For my main router in App.js, things work well. So I can visit my landing page (/), login page (/login), and register page (/register). And once I reach this page, if I do a manual refresh on my Chrome Browser (ctrl r), the page refresh and render accordingly.
Below is my App.js
import React from "react";
import { Router, Switch } from "react-router-dom";
import Login from "./components/Login";
import Register from "./components/Register";
import Dashboard from "./components/Dashboard";
import DynamicLayout from './router/DynamicLayout';
import LandingPage from './components/homepage/LandingPage';
import { history } from "./helpers/history";
const App = () => {
return (
<Router history={history}>
<div className="App">
<Switch>
<DynamicLayout
exact
path="/"
component={LandingPage}
layout="LANDING_NAV"
/>
<DynamicLayout
exact
path="/login"
component={Login}
layout="LOGIN_PAGE"
/>
<DynamicLayout
exact
path="/register"
component={Register}
layout="REGISTER_PAGE"
/>
<DynamicLayout
path="/dashboard"
component={Dashboard}
layout="DASHBOARD_PAGE"
/>
</Switch>
</div>
</Router>
);
};
export default App;
Below is my DynamicLayout.js
import React from "react";
import { BrowserRouter as Route, Switch } from "react-router-dom";
import Login from "../components/Login";
import Register from "../components/Register";
const DynamicLayout = (props) => {
const { component: RoutedComponent, layout, ...rest } = props;
const actualRouteComponent = <RoutedComponent {...props} />;
switch (layout) {
case "LANDING_NAV": {
return <div>{actualRouteComponent}</div>;
}
case "LOGIN_PAGE": {
return <div>{actualRouteComponent}</div>;
}
case "REGISTER_PAGE": {
return <div>{actualRouteComponent}</div>;
}
case "DASHBOARD_PAGE": {
return <div>{actualRouteComponent}</div>;
}
default: {
return (
<div>
<h2>Default Nav</h2>
{actualRouteComponent}
</div>
);
}
}
};
export default DynamicLayout;
The issue is with my nested router which is in my Dashboard component. Basically, once a admin user logged in, they will be shown the admin dashboard.
Below is my Dashboard component.
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { history } from "../helpers/history";
import { useHistory } from 'react-router-dom';
import {
BrowserRouter as Router,
Route,
Switch,
} from "react-router-dom";
import { logout } from "../actions/auth";
import AdminSideNavBar from "../components/admin/AdminSideNavBar";
import AdminManageUsers from "./admin/AdminManageUsers";
import AdminPendingApprovalUsers from "../components/admin/AdminPendingApprovalUsers";
import AdminDeactivatedUsers from "./admin/AdminDeactivatedUsers";
import AdminRegisterInternalUsers from "./admin/AdminRegisterInternalUsers";
import AdminLogs from "../components/admin/AdminLogs";
import BrokerSideNavBar from "../components/broker/BrokerSideNavBar";
import ShareholderSideNavBar from "../components/shareholder/ShareholderSideNavBar";
import Login from "../components/Login"
import AdminActivatedUsers from "./admin/AdminActivatedUsers";
const Dashboard = () => {
const [showAdminDashboard, setShowAdminDashboard] = useState(false);
const [showBrokerDashboard, setShowBrokerDashboard] = useState(false);
const [showShareholderDashboard, setShowShareholderDashboard] =
useState(false);
const { user: currentUser } = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
if (currentUser) {
setShowAdminDashboard(currentUser.roles.includes("ROLE_ADMIN"));
setShowBrokerDashboard(currentUser.roles.includes("ROLE_BROKER"));
setShowShareholderDashboard(
currentUser.roles.includes("ROLE_SHAREHOLDER")
);
}
}, [currentUser]);
const logOut = () => {
dispatch(logout());
};
let history = useHistory();
return (
<div>
{showAdminDashboard && (
<Router history= {history}>
<div className="wrapper">
<AdminSideNavBar />
<Switch>
<Route exact path="/dashboard" component={AdminPendingApprovalUsers} />
<Route exact path="/logs" component={AdminLogs} />
<Route exact path="/manageusers" component={AdminManageUsers} />
<Route exact path="/activeusers" component={AdminActivatedUsers} />
<Route exact path="/deactivatedusers" component={AdminDeactivatedUsers} />
<Route exact path="/registerinternalusers" component={AdminRegisterInternalUsers} />
</Switch>
</div>
</Router>
)}
{showBrokerDashboard && <BrokerSideNavBar />}
{showShareholderDashboard && <ShareholderSideNavBar />}
</div>
);
};
export default Dashboard;
With my side nav bar (AdminSideNavbar component), I can navigate to the various pages. Like /logs, /manageusers, /activeusers etc.
Below is my AdminSideNavBar component
import React from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { logout } from "../../actions/auth";
import {
CDBSidebar,
CDBSidebarContent,
CDBSidebarFooter,
CDBSidebarHeader,
CDBSidebarMenu,
CDBSidebarMenuItem,
} from "cdbreact";
import { NavLink } from "react-router-dom";
const AdminSideNavBar = () => {
const { user: currentUser } = useSelector((state) => state.auth);
const dispatch = useDispatch();
const { isLoggedIn } = useSelector((state) => state.auth);
const logOut = () => {
dispatch(logout());
};
return (
<div className="stickysidenav">
<CDBSidebar textColor="#fff" backgroundColor="#333">
<CDBSidebarHeader prefix={<i className="fa fa-bars fa-large"></i>}>
<a
href="/"
className="text-decoration-none"
style={{ color: "inherit" }}
>
TradeDuh
</a>
<p>{currentUser.username}</p>
{/* {isLoggedIn && (
<div className="wrapper">
<p>{currentUser.username}</p>
</div>
)} */}
</CDBSidebarHeader>
<CDBSidebarContent className="sidebar-content">
<CDBSidebarMenu>
<NavLink exact to="/dashboard" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="columns">Dashboard</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/logs" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="table">Logs</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/uploadcompany" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="edit">Update Nasdaq Stocks</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/manageusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="users-cog">Manage Users</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/activeusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="user-check">Active Users</CDBSidebarMenuItem>
</NavLink>
<NavLink exact to="/deactivatedusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="user-times">De-activated Users</CDBSidebarMenuItem>
</NavLink>
<NavLink to="/registerinternalusers" activeClassName="activeClicked">
<CDBSidebarMenuItem icon="user-plus">
Add Internal Users
</CDBSidebarMenuItem>
</NavLink>
<NavLink
exact
to="/login"
activeClassName="activeClicked"
onClick={logOut}
>
<CDBSidebarMenuItem icon="sign-out-alt">
Log Out
</CDBSidebarMenuItem>
</NavLink>
</CDBSidebarMenu>
</CDBSidebarContent>
<CDBSidebarFooter style={{ textAlign: "center" }}>
<div
style={{
padding: "20px 5px",
}}
>
TradeDuh (FDM - S21-Java-02)
</div>
</CDBSidebarFooter>
</CDBSidebar>
</div>
);
};
export default AdminSideNavBar;
The issue is, once I reach the various page, if I do a manual refresh (ctrl r), my whole screen will turn white/blank.
So say if I click on /logs, AdminLogs component is rendered, which is all good. BUT... if I now press ctrl r to do a manual fresh, the AdminLogs component don't show anymore. All I see is a blank white screen.
This is totally different from what is happening in my main router where I can do a manual page fresh and the page will render accordingly.
Any idea on how to solve this? What is my issue here?
Thank you for the help!
I am working on putting together a webpage with routing and I am having trouble getting redux to work. My goal is to send a GET response to the reducer but just to test the setup right now my goal is to send true. I can retrieve data from the redux store but I can't seem to send it and I am unsure where I might be going wrong. Here is what is supposed to happen:
Auth is checked in login or signup
App.js is wrapped in a provider
User can go to Cart.js and by clicking a button dispatch the value true
The can navigate to Menu.js and should be able to console.log the new value from the reducer
My problem: I can't seem to actually dispatch the true value. Nothing breaks but when I go to the Menu page, the console log shows the initial state of the reducer.
This has worked for me before in React Native. I'm wondering if I should be setting this up differently? or if authentication is messing things up?
Below is a sample of my code:
App.js
import React, { Component } from 'react';
import {
Route,
BrowserRouter as Router,
Switch,
Redirect,
} from "react-router-dom";
import Home from './pages/Home';
import Signup from './pages/Signup';
import Login from './pages/Login';
import Menus from './pages/Menus';
import Carts from './pages/Carts';
import Orders from './pages/Orders';
import Land from './pages/Land';
import { auth } from './services/firebase';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import ourReducer from './store/reducer';
const store = createStore(ourReducer);
global.api = 'https://pitapal.metis-data.site'
//global.api = 'http://localhost:3008';
function PrivateRoute({ component: Component, authenticated, ...rest }) {
return (
<Route
{...rest}
render={(props) => authenticated === true
? <Component {...props} />
: <Redirect to={{ pathname: '/login', state: { from: props.location } }} />}
/>
)
}
function PublicRoute({ component: Component, authenticated, ...rest }) {
return (
<Route
{...rest}
render={(props) => authenticated === false
? <Component {...props} />
: <Redirect to='/home' />}
/>
)
}
class App extends Component {
constructor() {
super();
this.state = {
authenticated: false,
loading: true,
};
}
componentDidMount() {
auth().onAuthStateChanged((user) => {
if (user) {
this.setState({
authenticated: true,
loading: false,
});
} else {
this.setState({
authenticated: false,
loading: false,
});
}
})
}
render() {
return this.state.loading === true ? <h2>Loading...</h2> : (
<Provider store={ store }>
<Router>
<Switch>
<Route exact path="/" component={Signup}></Route>
<PrivateRoute path="/home" authenticated={this.state.authenticated} component={Home}></PrivateRoute>
<PrivateRoute path="/menus" authenticated={this.state.authenticated} component={Menus}></PrivateRoute>
<PrivateRoute path="/carts" authenticated={this.state.authenticated} component={Carts}></PrivateRoute>
<PrivateRoute path="/order" authenticated={this.state.authenticated} component={Orders}></PrivateRoute>
<PublicRoute path="/signup" authenticated={this.state.authenticated} component={Signup}></PublicRoute>
<PublicRoute path="/login" authenticated={this.state.authenticated} component={Login}></PublicRoute>
</Switch>
</Router>
</Provider>
);
}
}
export default App;
Reducer.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
carts: 'nothing'
};
const ourReducer = (state = INITIAL_STATE, action) => {
const newState = { ...state };
switch (action.type) {
case "CARTS":
return {
...state,
carts: action.value
}
break;
}
return newState;
};
export default combineReducers({
reducer: ourReducer,
});
Carts.js
class Carts extends Component {
render() {
return (
<Container>
<Button onClick={()=>this.props.setCart(true)}>sendToRedux</Button>
</Container>
);
}
}
const mapStateToProps = (state) => {
const { reducer } = state
return { reducer }
};
const mapDispachToProps = dispatch => {
return {
setCart: (y) => dispatch({ type: "CARTS", value: y })
};
}
export default connect(mapStateToProps,
mapDispachToProps
)(Carts);
Menu.js
import React, { Component } from 'react';
import Header from "../components/Header";
import MenuItem from '../components/MenuItem';
import classes from './menus.module.css'
import { auth, db } from "../services/firebase";
import { Container, Col, Row } from 'react-bootstrap';
import { connect } from 'react-redux';
class Menu extends Component {
render() {
console.log('my carts data:', this.props.reducer.carts);
}
return (
<Container>
welcome to menu
</Container>
);
}
}
const mapStateToProps = (state) => {
const { reducer } = state
return { reducer }
};
export default connect(mapStateToProps)(Menu);
EDIT:
here is my screenshot from Redux Devtools, this means the dispatch is definitely being sent correct?
So it seems the issue is that when I navigate a page using my header component, state gets reloaded, the entire app is relaoded. Wondering if someone knows what it might be. Below is my header component:
Header.js
import { Link } from 'react-router-dom';
import { auth } from '../services/firebase';
//import "./header.css";
import React, { Component } from 'react'
import { Navbar, Nav, NavItem, NavDropdown, Form, FormControl, Button } from 'react-bootstrap'
// import {Link} from 'react-router-dom'
//import classes from './navbar.module.css';
import 'bootstrap/dist/css/bootstrap.min.css';
class Header extends Component {
render() {
return (
<div>
{auth().currentUser
?
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="/login">Home</Nav.Link>
<Nav.Link href="/menus">Manage my Menus</Nav.Link>
</Nav>
<Nav>
<Button onClick={() => auth().signOut()} variant="outline-success">Sign Out</Button>
</Nav>
</Navbar.Collapse>
</Navbar>
:
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="/login">Login</Nav.Link>
<Nav.Link href="/signup">Sign Up</Nav.Link>
</Nav>
<Nav>
<Nav.Link href="contact">Contact Us</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
}
</div>
)
}
}
export default Header;
Issue
Your Header is using plain anchor tags to issue navigation to your route pages which has the side-effect of also doing a page load.
Solution
Use the react-router-dom Link component for linking.
Either specify the as prop of the Nav.Link or use Link
<Nav.Link as={Link} to="/login">Home</Nav.Link>
or
<Link to="/login">Home</Link>
class Header extends Component {
render() {
return (
<div>
{auth().currentUser
?
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Link to="/login">Home</Link>
<Link to="/menus">Manage my Menus</Link>
</Nav>
<Nav>
<Button onClick={() => auth().signOut()} variant="outline-success">Sign Out</Button>
</Nav>
</Navbar.Collapse>
</Navbar>
:
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Link to="/login">Login</Link>
<Link to="/signup">Sign Up</Link>
</Nav>
<Nav>
<Link to="contact">Contact Us</Link>
</Nav>
</Navbar.Collapse>
</Navbar>
}
</div>
)
}
}
I'm new in react and redux and im trying to create a private nested route .On Login i get redirected to /userdashboard also the token gets back from the server and gets stored to the local storage.
Whenever Im in http://localhost:3000/userdashboard/post the user gets loaded the posts get loaded.
But when i refresh the AUTH_ERROR action gets dispatched and the token gets removed from the local storage and i get redirected.
I've tried to call the same action for loading the user on the nested component but still the AUTH_ERROR action was dispatched
My App.js
import React, { Fragment, useEffect } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Landing from './components/layout/Landing/Landing';
import Register from './components/auth/Register';
import Login from './components/auth/Login';
import Alert from './components/layout/Alert/Alert';
import UserDasboard from './components/userdashboard/UserDashboard';
import PrivateRoute from './components/routing/PrivateRoute';
//Redux
import { Provider } from 'react-redux';
import store from './store';
import { loadUser } from './actions/auth';
import setAuthToken from './utils/setAuthToken';
import './App.scss';
if (localStorage.token) {
setAuthToken(localStorage.token);
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser());
}, []);
return (
<Provider store={store}>
<Router>
<Fragment>
<Alert />
<Switch>
<PrivateRoute path='/userdashboard' component={UserDasboard} />
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
<Route exact path='/' component={Landing} />
</Switch>
</Fragment>
</Router>
</Provider>
);
};
export default App;
//UserDashboard
import React, { Fragment, useEffect, useState } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import PropTypes from 'prop-types';
import Navbar from '../layout/Navbar/Navbar';
import SideBar from '../layout/SideBar/SideBar';
import Posts from '../../components/posts/Posts';
const UserDashboard = props => {
const [showSidebar, setShowSidebar] = useState(true);
const toggleSideBar = () => setShowSidebar(!showSidebar);
return (
<Router>
<Fragment>
<Navbar />
<div className='columns is-mobile'>
<SideBar showSidebar={showSidebar} />
<div className='column'>
<div className='columns is-mobile'>
<div
className='column is-2-desktop is-2-mobile'
onClick={toggleSideBar}
>
<button></button>
</div>
<div className='column is-3-desktop is-3-mobile is-offset-4'>
Dashboard
</div>
</div>
<Switch>
<Route path={props.match.url + '/post'} component={Posts} />
<Route
path={props.match.url + '/new-post'}
render={() => <p>Hello</p>}
/>
</Switch>
</div>
</div>
</Fragment>
</Router>
);
};
UserDashboard.propTypes = {};
export default UserDashboard;
//Post
import React, { Fragment, useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import PostItem from './PostItem';
import { getPosts } from '../../actions/post';
const Posts = ({ getPosts, post: { posts, loading } }) => {
useEffect(() => {
getPosts();
}, [getPosts]);
return loading ? (
<p>Loading</p>
) : (
<Fragment>
<section className='section'>
<div className='columns'>
{/* Post form*/}
<div className='columns'>
{posts.map(post => (
<PostItem
key={post._id}
post={post}
subs={post.subscribedusers}
/>
))}
</div>
</div>
</section>
</Fragment>
);
};
Posts.propTypes = {
getPosts: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post
});
export default connect(
mapStateToProps,
{ getPosts }
)(Posts);
//PostItem
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import Moment from 'react-moment';
import { connect } from 'react-redux';
import { addSubscriber, removeSubscriber } from '../../actions/post';
const PostItem = ({
addSubscriber,
removeSubscriber,
auth,
post: {
_id,
posttitle,
posttext,
postimage,
user,
subscribedusers,
userposts,
date
},
subs
}) => {
const sub = subs.map(sub => sub);
return (
<div className='column'>
<Link to={`/post/${_id}`}>
<div class='card'>
<div class='card-image'>
<figure class='image is-4by3'>
<img src={postimage} alt='Placeholder image' />
</figure>
</div>
<div class='card-content'>
<div class='media'>
<div class='media-left'>
<figure class='image is-48x48'>
<img
src='#'
alt='Placeholder image'
/>
</figure>
</div>
<div class='media-content'>
<p class='title is-4'>{posttitle}</p>
{/* <p class='subtitle is-6'>#johnsmith</p> */}
</div>
</div>
<div class='content'>
<p>{posttext}</p>
<br />
<time datetime='2016-1-1'>
<Moment format='YYYY/MM/DD'>{date}</Moment>
</time>
</div>
<footer class='card-footer'>
<p class='card-footer-item'>
Subscriptions{' '}
{subscribedusers.length > 0 && (
<span>{subscribedusers.length}</span>
)}
</p>
<p class='card-footer-item'>{userposts.length}</p>
<button className='button' onClick={e => addSubscriber(_id)}>
Subscribe
</button>
<button className='button' onClick={e => removeSubscriber(_id)}>
UnSubscribe
</button>
</footer>
</div>
</div>
</Link>
</div>
);
};
PostItem.propTypes = {
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(
mapStateToProps,
{ addSubscriber, removeSubscriber }
)(PostItem);
//loadUser in ./actions/auth
export const loadUser = () => async dispatch => {
if (localStorage.token) {
setAuthToken(localStorage.token);
}
try {
const res = await axios.get('userapi/auth');
dispatch({
type: USER_LOADED,
payload: res.data
});
} catch (err) {
dispatch({
type: AUTH_ERROR
});
}
};
//setAuthToken
import axios from 'axios';
const setAuthToken = token => {
if (token) {
axios.defaults.headers.common['x-auth-token'] = token;
} else {
delete axios.defaults.headers.common['x-auth-token'];
}
};
export default setAuthToken;