I have logout component(stateless) in that I want to delete access_token,
But my component not get initialised and there is no routing error as well,
Routing :
<Route path="/logout" component={LogoutComponante} />
logout component:
import React from 'react'
import { connect } from 'react-redux'
import { LOGOUT } from '../constant/constant'
function LogoutComponante(props){ console.log('in logout');
// props.logoutmethod();
// props.history.push("/")
return <h1>abc</h1>;
}
const mapStateToProps = state => {
return {
access_token : state.access_token
}
}
const mapDispatchToProps = dispatch => {
return {
logoutmethod : () => {
dispatch({ type : LOGOUT })
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(LogoutComponante)
Routing details:
<Switch>
<Route exact path="/" component= {Home} />
<RouterManager/>
</Switch>
RouterManager component :
const RouterManager = (props) => {
if(props.access_token){
return (
<div>
<Route path="/logout" component={LogoutComponante} />
</div>
)
}else{
return (
<div>
<Route path="/register" component={Register} />
<Route path="/login" component={Register} />
</div>
)
}
}
when I visit /logout it returns nothing not even console.log('in logout'); is working.
What I am doing wrong?
Try wrapping your redux component with withRouter. Sometimes you will not see ok changes:
import { withRouter, connect } from 'react-redux'
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(LogoutComponante))
Related
How do I pass the id from /profile/:id to the function generateProfile(findProfile(id))
Below is my code
import { Routes, Route } from 'react-router-dom';
import Profile from './Profile';
import '../assets/styles/App.css';
import seedProfiles from '../seedProfiles';
import generateProfile from '../helpers/profileHelper';
function App() {
const findProfile = id => seedProfiles.find(profile => profile.id === id);
return (
<Routes>
<Route exact="true" path="/" element={<h1>PROFILES</h1>} />
<Route
exact="true"
path="/profile/:id"
element={<Profile profile={generateProfile(findProfile(???))} />}
/>
</Routes>
);
}
export default App;
Thanks
Route path parameters are only accessible via the useParams hook. If you need to access the route path parameters prior to rendering the routed component then you'd need to create a wrapping component that can "sniff" the parameters and apply any logic.
Example:
const ProfileWrapper = ({ getProfile }) => {
const { id } = useParams();
return <Profile profile={getProfile(id)} />
};
function App() {
const getProfile = id => generateProfile(
seedProfiles.find(profile => profile.id === id)
);
return (
<Routes>
<Route path="/" element={<h1>PROFILES</h1>} />
<Route
path="/profile/:id"
element={<ProfileWrapper profile={getProfile} />}
/>
</Routes>
);
}
However, it's more common to move this logic into the component being rendered since there are not really any parental dependencies.
Example:
import { Routes, Route } from 'react-router-dom';
import Profile from './Profile';
import '../assets/styles/App.css';
import seedProfiles from '../seedProfiles';
import generateProfile from '../helpers/profileHelper';
const Profile = () => {
const { id } = useParams();
const profile = generateProfile(seedProfiles.find(profile => profile.id === id));
return ...;
}
import { Routes, Route } from 'react-router-dom';
import Profile from './Profile';
import '../assets/styles/App.css';
function App() {
return (
<Routes>
<Route path="/" element={<h1>PROFILES</h1>} />
<Route path="/profile/:id" element={<Profile />} />
</Routes>
);
}
I can't really see it but is there something wrong with
promotion={this.props.promotions.filter(promotion=>promotion.featured)[0]}
I keep getting this error, and I'm having trouble figuring it out:
TypeError: can't access property "filter", this.props.promotion is undefined. its suposed to me filtering an object in props.its so the state from promotions is sent i think
import React, { Component } from 'react';
import Home from './HomeComponent';
import { Switch, Route, Redirect, withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import Directory from './DirectoryComponent';
import CampsiteInfo from './CampsiteInfoComponent';
import Header from './HeaderComponent';
import Footer from './FooterComponent';
import Contact from './ContactComponent';
import About from './AboutComponent';
import { addComment } from '../redux/ActionCreators';
const mapStateToProps = state => {
return {
campsites: state.campsites,
comments: state.comments,
partners: state.partners,
promotion: state.promotion
}
}
const mapDispatchToProps = {
addComment: (campsiteId, rating, author, text) => (addComment(campsiteId, rating, author, text))
};
class Main extends Component {
render() {
const HomePage = () => {
return (
<Home
campsite={this.props.campsites.campsites.filter(campsite=>campsite.featured)[0]}
promotion={this.props.promotions.promotions.filter(promotions=>promotions.featured)[0]}
partner={this.props.partners.partners.filter(partners=> partners.featured)[0]}
/>
);
};
const CampsiteWithId = ({match}) => {
return (
<CampsiteInfo
campsite={this.props.campsites.filter(campsite=> campsite.id === +match.params.campsiteId)[0]}
comments={this.props.comments.filter(comment => comment.campsiteId === +match.params.campsiteId)}
addComment={this.props.addComment}
/>
);
};
return (
<div>
<Header />
<Switch>
<Route path='/home' component={HomePage} />
<Route exact path='/directory' render={()=> <Directory campsites={this.props.campsites} />} />
<Route path='/directory/:campsiteId' component={CampsiteWithId} />
<Route exact path='/contactus' component={Contact} />
<Route exact path='/aboutus' render={()=> <About partners={this.props.partners} />} />
<Redirect to='/home' />
</Switch>
<Footer />
</div>
);
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));
This could happen if props dont have promotions in the initial render. Do some validation like below. Do similar validations for campsite and partners if needed.
<Home
campsite={
this.props.campsites.campsites.filter((campsite) => campsite.featured)[0]
}
promotion={
this.props.promotions &&
Array.isArray(this.props.promotions.promotions) &&
this.props.promotions.promotions.length
? this.props.promotions.promotions.filter(
(promotions) => promotions.featured
)[0]
: ""
}
partner={
this.props.partners.partners.filter((partners) => partners.featured)[0]
}
/>;
In AppRouter, I have a conditional route with redirect for <AdminLayout/>.
relevant snippet:
<Route
exact
path="/admin"
strict
render={(props) => <AdminLayout {...props} />}
>
{loggedIn ? <Redirect to="/admin/summary" /> : <Login />}
</Route>
If loggedIn is true then, redirect to /admin/summary else redirect it back to <Login/>
The problem is: it is only changing the URL but not rendering the <AdminLayout/>.
Not sure where I am going wrong and what I am missing.
UPDATED PrivateRoute and AppRouter below
AppRouter
import React, { useEffect } from "react";
import { Router, Route, Switch, Redirect } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { createBrowserHistory } from "history";
import { alertActions } from "../actions";
import { history } from "../helpers";
import AdminLayout from "layouts/Admin/Admin.js";
import AuthLayout from "layouts/Auth/Auth.js";
import ResetPassword from "../components/pages/reset-password/ResetPassword";
import MailReset from "../components/pages/reset-password/MailReset";
import PrivateRoute from "../routes/PrivateRoute";
import Dashboard from "views/Dashboard";
const hist = createBrowserHistory();
const AppRouter = () => {
const alert = useSelector((state) => state.alert);
const dispatch = useDispatch();
useEffect(() => {
history.listen((location, action) => {
// clear alert on location change
dispatch(alertActions.clear());
});
}, []);
return (
<Router history={hist}>
<Switch>
{/* <Route path="/admin" render={(props) => <AdminLayout {...props} />} /> */}
<PrivateRoute exact path="/admin">
<Dashboard />
</PrivateRoute>
<Route
path="/auth/login"
render={(props) => <AuthLayout {...props} />}
/>
<Route exact path="/auth/forgotPassword" component={ResetPassword} />
<Route exact path="/auth/mail_reset" component={MailReset} />
<Redirect from="*" to="/auth/login" />
</Switch>
</Router>
);
};
export default AppRouter;
PrivateRoute
import React from "react";
import { Route, Redirect } from "react-router-dom";
import AdminLayout from "../layouts/Admin/Admin";
function PrivateRoute({ component: Component, roles, ...rest }) {
console.log("rest pvt route", ...rest);
return (
<Route
{...rest}
render={(props) => {
console.log("propsssss", props);
// if (!localStorage.getItem('userid')) {
if (!localStorage.getItem("access_token")) {
// not logged in so redirect to login page with the return url
return (
<Redirect
to={{ pathname: "/auth/login", state: { from: props.location } }}
/>
);
}
// logged in so return component
return <AdminLayout {...props} />;
}}
/>
);
}
export default { PrivateRoute };
So trying to explain what its is wrong:
You are setting rendering child and render props that's why children props takes priority here:
<Route
exact
path="/admin"
render={(props) => <AdminLayout {...props} />}
>
{loggedIn ? <Redirect to="/admin/summary" /> : <Login />}
</Route>
Your private route is correct but need to add your layout as well:
return <AdminLayout {...props} /><Component {...props} /></AdminLayout/>;
Inside app route you need to import PrivateRoute component it will look like this:
import PrivateRoute from './PrivateRoute';
const AppRouter = () => {
const alert = useSelector((state) => state.alert);
const loggedIn = useSelector((state) => state.authentication.loggedIn);
const dispatch = useDispatch();
useEffect(() => {
history.listen((location, action) => {
// clear alert on location change
dispatch(alertActions.clear());
});
}, []);
return (
<Router history={hist}>
<Switch>
<PrivateRoute exact path='/admin'>
<YOUR AUTH COMPONENT WHICH YOU WANT TO RENDER />
</PrivateRoute>
<Route
path='/auth/login'
render={(props) => <AuthLayout {...props} />}
/>
<Route exact path='/auth/forgotPassword' component={ResetPassword} />
<Route exact path='/auth/mail_reset' component={MailReset} />
<Redirect from='*' to='/auth/login' />
</Switch>
</Router>
);
};
Here I created demo code of this. Take reference from it: https://codesandbox.io/s/react-router-redirects-auth-forked-6q6o4?file=/example.js
I want to protect some urls so users can't access them without login in first. This is my Auth.js
export const AuthContext = React.createContext();
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
firebase.auth().onAuthStateChanged(setCurrentUser);
}, []);
return (
<AuthContext.Provider
value={{ currentUser }}
>
{children}
</AuthContext.Provider>
);
};
And this is my App.jsx
function App() {
return (
<AuthProvider>
<Router>
<div>
<Route path="/citas" exact>
<CitasPage />
</Route>
<Route path="/citasDetalladas" exact>
<CitaDetallada />
</Route>
<Route path="/crearCita" exact>
<CrearCitasPage />
</Route>
<PrivateRoute path="/index" exact >
<Index />
</PrivateRoute>
</div>
</Router>
</AuthProvider>
);
}
export default App;
Since I'm new to React, I've been following a tutorial for doing the login and logout using Firebase and React, but it didn't help with this issue. Any ideas?
You can use this auth flow to protect routes from unauthenticated users.
I have created global context with maintain currentUser.
By this currentUser, routes are decided.
AuthProvider.js
import React, { createContext, useState, useEffect } from "react";
import firebase from "./firebase";
export const AuthContext = createContext();
export function AuthProvider({ children }) {
useEffect(() => {
setIsLoading(true);
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setCurrentUser(user);
setIsLoading(false);
} else {
setIsLoading(false);
setCurrentUser(null);
}
});
}, [setCurrentUser, setRole, setIsLoading]);
return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
);
App.js
import React from "react";
import { HashRouter as Router } from "react-router-dom";
import { AuthProvider } from "./data/auth";
function App() {
return (
<AuthProvider>
<Router>
<Routes />
</Router>
</AuthProvider>
);
}
export default App;
Routes.js
import React, { useContext } from 'react'
import { Switch, Redirect, Route } from 'react-router-dom';
import { AuthContext } from "../data/auth";
const Routes = () => {
const { currentUser } = useContext(AuthContext);
if (currentUser) {
return (
<Switch>
<Route path='/dashboard' component={dashboard} />
<Route path='/report' component={report} />
<Route path='/profile' component={profile} />
<Redirect to='/report' />
</Switch>
)
} else {
return (
<Switch>
<Route path='/login' component={login} />
<Route path='/register' component={register} />
<Redirect to='/login' />
</Switch>
)
}
}
export default Routes;
You need to do something to determine whether the user is currently authenticated. I'm not sure exactly how firebase.auth() works but you need to check on each route (or use a higher-order component) whether the user is authenticated and if not, return <Redirect to='somewhereElse' />
import React, { Component } from "react";
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
import { library } from "#fortawesome/fontawesome-svg-core";
import {
faHome,
faClock,
faTasks,
faStickyNote,
faCalendarWeek
} from "#fortawesome/free-solid-svg-icons";
import { connect } from "react-redux";
import store from "./store";
import { loadUser } from "./actions/authActions";
import Home from "./Home";
import SideNav from "./Components/SideNav";
import Recent from "./Components/Recent";
import TopBar from "./Components/TopBar";
import AddNote from "./AddNote";
import LogIn from "./Components/LogIn/LogIn.js";
import Register from "./Components/Register/Register";
import ToDo from "./Components/ToDo/ToDo";
import { timingSafeEqual } from "crypto";
library.add(faHome, faClock, faTasks, faStickyNote, faCalendarWeek);
class App extends Component {
componentDidMount() {
store.dispatch(loadUser());
}
componentDidUpdate(prevProps) {
if(this.props != this.prevProps) {
console.log("hello")
}
}
LogInContainer = () => {
return <Route path="/login" component={LogIn} />;
};
RegisterContainer = () => {
return <Route path="/register" component={Register} />;
};
DefaultContainer = () => {
return (
<div className="app_container">
<SideNav />
<TopBar />
<Route exact path="/" component={Home} />
<Route path="/recent" component={Recent} />
<Route path="/AddNote" component={AddNote} />
<Route path="/ToDo" component={ToDo} />
</div>
);
};
// Check for authenticaition
AuthRoute = ({ component: Component, props, ...rest }) => {
return (
<Route
{...rest}
render={props => {
if (this.props.auth.isAuthenticated) {
return <Component {...props} />;
}
else {
return (
<Redirect
to={{
pathname: "/login",
state: { from: this.props.location }
}}
/>
);
}
}}
/>
);
};
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/login" component={this.LogInContainer} />
<Route exact path="/register" component={this.RegisterContainer} />
<this.AuthRoute component={this.DefaultContainer} />
</Switch>
</BrowserRouter>
);
}
}
const mapStateToProps = (state) => ({
auth: state.auth
})
export default connect(mapStateToProps)(App);
How can app.js receive the new state from redux after logging in? The initial fetch it will get isAuthenticated = false. User then log ins but app.js isn't getting the new state. Am I implementing authenitcation wrong? comonentDidUpdate is throwing an error when trying to update props but feel like this is a bad way of doing it anyways