How to reroute my react app after an asynchronous api request - javascript

I am working on rerouting my application. I have a form, I want the user to be redirected after the form is submitted successfully. I placed the logic after the api is successful. My url changes but the component that is meant to be loaded on that url does not load.
import { browserHistory } from '../router/router';
...
export const storeRecord=(data)=>{
return function(dispatch, getState){
//console.log('state',getState());
const {authToken, userId} = getState().authReducer;
token= authToken;
data.userId = userId;
const url = 'investment/store';
apiCall(url, data, dispatch,'post').then((data)=>{
try{
if(data.status=== 200){
//let data = apiResponse.data
console.log('success',data);
browserHistory.push('/');
//dispatch(push('/'));
}
}
catch(error){
console.log('returnedError',error);
}
}, (error)=>{console.log(error)});
}
}
Here is my route as well:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { createBrowserHistory } from 'history';
...
export const browserHistory = createBrowserHistory();
const AppRouter=()=>{
return(<Router>
<LoaderModal />
<Switch>
<Route path="/" exact component={LandingPage} />
<PublicRouter path="/register" exact component={SignupPage} />
<PublicRouter path="/login" exact component={LoginPage} />
<PrivateRoute path="/investment/new" exact component={InvestmentForm} />
<Route component={NotFoundPage} />
</Switch>
</Router>)
}
export default AppRouter;
I am using react-router-dom for rerouting and history for the browser re-routing.

You can use following code in place of dispatch(push('/')) which you used in storeRecord component.
window.location.assign('/');

You need to use the same history object that your Router modules uses to dynamically Route.
The easiest solution is for you to not use browserRouter but to use a Router with custom history
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import { createBrowserHistory } from 'history';
...
export const browserHistory = createBrowserHistory();
const AppRouter=()=>{
return(<Router history={browserHistory}>
<LoaderModal />
<Switch>
<Route path="/" exact component={LandingPage} />
<PublicRouter path="/register" exact component={SignupPage} />
<PublicRouter path="/login" exact component={LoginPage} />
<PrivateRoute path="/investment/new" exact component={InvestmentForm} />
<Route component={NotFoundPage} />
</Switch>
</Router>)
}
export default AppRouter;
and then your action will look like
import { browserHistory } from '../router/router';
...
export const storeRecord=(data)=>{
return function(dispatch, getState){
const {authToken, userId} = getState().authReducer;
token= authToken;
data.userId = userId;
const url = 'investment/store';
apiCall(url, data, dispatch,'post').then((data)=>{
try{
if(data.status=== 200){
browserHistory.push('/');
}
}
catch(error){
console.log('returnedError',error);
}
}, (error)=>{console.log(error)});
}
}

Related

React router v6: Protected Routes displaying after logging out

I am attempting to implement a private route in React; the homepage should not be visible until the user logs in. If I restart my frontend, all protected routes are not accessible until the user logs in. However, after the first login, all protected routes don't seem to be protected; I can logout, the session is destroyed in my database and my backend sends a response of {isLoggedIn: false}, but for some reason I can still access the protected routes.
When I didn't use 'useState', I could login and my backend would confirm I was logged in, but I still couldn't access any protected routes. This is the closest I've got to my end goal, but obviously still doesn't work. Any help would be appreciated.
Private Routes
import { useState, useEffect } from 'react';
import React from 'react';
import axios from 'axios';
const checkIfLogged = async () => {
let[logged, setLogged] = useState(false);
await axios.get("http://localhost:3001/auth", {
withCredentials: true
}).then((res) => {
setLogged(res.data.isLoggedIn);
})
return logged;
}
const updateAuth = async(check) => {
const loggedIn = await check;
return loggedIn;
}
const PrivateRoutes = () =>{
const loggedIn = updateAuth(checkIfLogged);
return(
loggedIn ? <Outlet /> : <Navigate to="/login" />
)
}
export default PrivateRoutes;
Auth Check
app.get("/auth", (req, res) => {
if(req.session.isAuth){
res.send({isLoggedIn: true})
}
else{
res.send({isLoggedIn: false})
}
})
App.js
import{
Routes,
Route,
} from "react-router-dom";
import React from "react";
import Home from "./screens/Home";
import About from "./screens/About";
import Login from "./screens/Login";
import Register from "./screens/Register";
import Logout from "./screens/Logout";
import PrivateRoutes from "./utils/private";
const App = () => {
return (
<>
<Routes>
<Route element={<PrivateRoutes/>}>
<Route path="/" exact element={<Home />}/>
<Route path="/about" element={<About />}/>
</Route>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />}/>
<Route path="/logout" element={<Logout />}/>
</Routes>
</>
);
}
export default App;
Another solution is by actually make a conditional rendering based on if the user is admin, then the page is rendered, otherwise a null is returned:
so in your PrivateRoutes:
import { useState, useEffect } from 'react';
import React from 'react';
import axios from 'axios';
const PrivateRoute = async () => {
let[logged, setLogged] = useState(false);
let [isSuperUser,setIsSuperUser] = useState(false) // adding new state
useEffect(()=>{
axios.get("http://localhost:3001/auth", {
withCredentials: true
}).then((res) => {
setLogged(res.data.isLoggedIn);
setIsSuperUser(res.data.isSuperUser)
})
},[isLoggedIn])
return (
isLoggedIn&& isSuperUser?<Outlet/>:null
)
}
export default PrivateRoutes;
Edit: this solution is valid, however, I added a 2nd solution that match the OP style.
First of all, checkLoggedIn is a component, so it need to be capitalized, fix it to CheckLoggedIn
Second of all, I am not sure what backend you are using, but I would check if the user is an admin/superuser all in App.js, since we are dealing with routes here, and then based on that I will let them access the protected route:
in App.js
import{
Routes,
Route,
} from "react-router-dom";
import React from "react";
import {useEffect,useState} from React;
import Home from "./screens/Home";
import About from "./screens/About";
import Login from "./screens/Login";
import Register from "./screens/Register";
import Logout from "./screens/Logout";
import PrivateRoutes from "./utils/private";
const App = () => {
let[logged, setLogged] = useState(false)
let [isSuperUser,setIsSuperUser] = useState(false)
useEffect(()=>{
axios.get('localhost:3001/auth/',{withCredentials:true}).then((res)=>{
// send a response if the current logged in user is a super user
setLogged(res.data.isLoggedIn)
setIsSuperUser(res.data.isSuperUser)
}
},[logged]) // useEffect will get re-invoked whenever the user log out, or their session is ended
// now for the last part, I would make a conditional rendering, where a user will access route only if they are superuser/admin
return (
<>
<Routes>
<Route element={isSuperUser?<PrivateRoutes/>:''}>
<Route path="/" exact element={<Home />}/>
<Route path="/about" element={<About />}/>
</Route>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />}/>
<Route path="/logout" element={<Logout />}/>
</Routes>
</>
);
}
export default App;
Notice the first route, you will only be able to access it if you are a logged in admin.
you also need to notice that what define a react component is 2 things:
Capitalization of your functional component name
it has to return a JSX

React-router-dom Protected Routes are not working

Protected Routes.js:
In protected routes you can see I'm using directly false in if statement but I'm still able to see that page why?
import React from 'react';
import { Route } from 'react-router-dom';
// import Auth from './User/Auth';
import Error401 from './Error/401';
// create a component for protected route
console.log('Routes.js');
export const ProtectedRoute = ({ element: Element, ...rest }) => {
console.log("Function Called")
return (
<Route {...rest} render={props => {
if(false){
return <Element {...props} />
}else{
return <Error401 />
}
}
} />
)
}
App.js:
This is app.js where I'm using protected routes component
import './App.css';
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Layout } from 'antd';
import { MoneyCollectOutlined } from '#ant-design/icons';
import Login from './Components/User/Login';
import Signup from './Components/User/Signup';
import {ProtectedRoute} from './Components/Routes';
import Error404 from './Components/Error/404';
function App() {
return (
<BrowserRouter>
<Layout style={{minHeight:"100vh"}}>
<Layout.Header>
<h1 style={{color:"white"}} align="center"> <MoneyCollectOutlined/>MoneyG</h1>
</Layout.Header>
<Layout.Content style={{minHeight:"100%"}}>
<Routes>
<ProtectedRoute exact path="/register" element={<Signup/>} />
<ProtectedRoute exact path="/login" element={<Login/>} />
<Route path="*" element={<Error404/>} />
</Routes>
</Layout.Content>
</Layout>
</BrowserRouter>
);
}
export default App;
First, <Routes> elements should only have <Route> elements as children. You should move your protection logic down a layer.
Secondly, the render prop doesn't exist anymore in V6. It was replaced in favor of element. See doc.
Here is how you might tackle it:
<Routes>
<Route exact path="/register" element={(
<ProtectedRoute>
<Signup/>
</ProtectedRoute>
)} />
<Route exact path="/login" element={(
<ProtectedRoute>
<Login/>
</ProtectedRoute>
)} />
<Route path="*" element={<Error404/>} />
</Routes>
And:
const ProtectedRoute = () => {
if (condition) { return <Error401 />; } // You might as well use Navigate here
return children;
};
you can use createContext & useContext
//store/AuthApi.jsx
import { createContext } from "react";
const AuthApi = createContext();
export default AuthApi;
Then define the context app.jsx
import React, from 'react'
import { AllRoutes } from 'routes/Routes';
import { BrowserRouter as Router } from "react-router-dom";
import AuthApi from 'store/AuthApi';
const App = () => {
const [user, setUser] = useState(false);
useEffect(() => {
// you can get user from localStorage or Cookie(js-cookie npm)
//then you can change user state true or false
}, [])
return (
<>
<AuthApi.Provider value={{ user, setUser }}>
<Router>
<AllRoutes />
</Router>
</AuthApi.Provider>
<Toast />
</>
)
}
export default App
then see AllRoutes
//routes/Routes
import React, { useContext } from "react";
import { Routes, Route } from "react-router-dom";
import { SignIn, SignUp, Dashboard } from "pages";
import AuthApi from "store/AuthApi";
export const ProtectedRouting = () => {
return (
<Routes >
<Route path='/' exact element={<Dashboard />} />
// add more protected routes
</Routes>
)
}
export const AuthRouting = () => {
return (
<Routes >
<Route exact={true} path='/sign-in' element={<SignIn />} />
<Route exact={true} path='/sign-up' element={<SignUp />} />
</Routes>
)
}
export const AllRoutes = ()=> {
const context = useContext(AuthApi);
console.log(context.user)
return (
context.user ?
<ProtectedRouting />
: <AuthRouting />
)
}
pages/SignIn.jsx
import React,{ useContext } from 'react';
import AuthApi from "store/AuthApi";
const SignIn = () => {
const context = useContext(AuthApi);
const signInSubmit =(e)=> {
e.preventDefault();
//post request to signin
// if login is successfull then save user or token in cookie or localStorage or something
context?.setUser(true);
//...
}
return (
//signin code here
<form onSubmit={signInSubmit}>
///input here
</form>
)
}
export default SignIn

How to navigate outside of components using react-router-dom and BrowserRouter

I am using BrowserRouter and i want to navigate outside of component. I can navigate using Router but can i do using BrowserRouter
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
<Router history = {history}>
<Switch>
<Route
path="/"
component={Home}
exact={true} />
<Route
path="/somePath"
component={SomeDisplay}
exact={true} />
</Router>
And I can use it outside component as
import { history } from 'path/to/RouterConfigurations';
export const func1 = () => {
history.push({
pathname: "/somePath"
});
}

Routes are not working with React router v4

I have upgraded to react-router v4, React v16, react-router-redux v5.0.0-alpha.9. After going through the tutorials in internet I finally end up with configuring my routes, Redux store and history as like below but the routes are not working. If I click on login/register or other links it is taking me to NotFound component always.
I am very new to these latest versions.
Routes.js
import React from 'react';
import {default as history} from './history';
import { Switch, Redirect, IndexRedirect, IndexRoute, BrowserRouter as Router, Route, Link } from 'react-router-dom';
import {Provider} from 'react-redux';
import store from './store'
import {Map, toJS} from 'immutable';
import TransparentIndexPage from './Navigation/Components/TransparentIndexPage'
// Import miscellaneous routes and other requirements
import App from './App';
import NotFound from './Layout/Components/NotFound';
// Import static pages
import Home from './Layout/Components/Home';
import Contact from './Contact/Components/Contact';
import Settings from './Navigation/Components/Settings';
import CreatePage from './CreatePage/Components/CreatePage';
import {getCurrentUser, extractRoleInfo} from './Login/utils'
import Careers from './About/Components/Careers';
import Team from './About/Components/Team';
import Press from './About/Components/Press';
import Policy from './About/Components/Policy';
// import About from './About/Components/About';
// Import authentication related pages
import Login from './Login/Components/Login';
import ProfileView from './MyProfile/Components/ProfileView';
import Confirmation from './Email/Components/Confirmation';
import About from './About/Components/About';
import Register from './Register/Components/Register';
// import Facebook from './Facebook/Components/FacebookLogin';
import Logout from './Logout/Components/Logout';
import Profile from './Profile/Components/Profile';
// import UserDropdown from './Layout/Components/UserDropdown';
import ForgotPassword from './ForgotPassword/Components/ForgotPassword';
import ResetPassword from './ResetPassword/Components/ResetPassword';
import {syncHistoryWithStore} from 'react-router-redux'
// Import dashboard pages
import Dashboard from './Dashboard/Components/Dashboard';
import Search from './Search/Components/Search';
import Post from './Post/Components/Post';
import * as loginActions from './Login/actions';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux'
// const history = createHistory();
// const history = syncHistoryWithStore(browserHistory, store, {
// selectLocationState: state => state.get('Routes').toJS()
// })
// console.log("History: ", JSON.stringify(history));
function redirectIfAuth(nextState, replace) {
const user = getCurrentUser(store.getState())
if (user.get('id')) {
replace({
pathname: 'dashboard',
state: { nextPathname: nextState.location.pathname}
})
}
}
var update = 0
function checkRoles(nextState, replace) {
const user = getCurrentUser(store.getState())
console.log("Role extract user: ", JSON.stringify(extractRoleInfo(user.get('role'))))
if (!extractRoleInfo(user.get('role'))) {
var url = window.location.href
var refURL = ''
let x = window.location.href.split('/')
for(let v=5; v<x.length; v++)
{
refURL += x[v]
}
if(refURL)
{
if(update == 0)
{
update = 1
store.dispatch(loginActions.setURL(refURL))
}
}
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
})
}
}
const routes = (
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<Route exact path="/" component={App} />
<Switch>
<Route path="login" component={Login} onEnter={redirectIfAuth}/>
<Route path="contact" component={Contact} />
<Route path="home" component={Home} />
<Route path="about" component={About} />
<Route path="careers" component={Careers} />
<Route path="press" component={Press} />
<Route path="policy" component={Policy} />
<Route path="team" component={Team} />
<Route path="home" component={Home} />
<Route path="register" component={Register} />
<Route path="about" component={About} />
<Route path="forgotpassword" component={ForgotPassword} onEnter={redirectIfAuth}/>
<Route path="resetpassword/:resetToken" component={ResetPassword}/>
<Route path="confirmation/:token" component={Confirmation} />
<Route path="dashboard" name='Dashboard' component={Dashboard} onEnter={checkRoles}/>
<Route path="/:id/myProfile" name='ProfileView' component={ProfileView} onEnter={checkRoles}/>
<Route path="create-page" name='ProfileView' component={CreatePage} onEnter={checkRoles}/>
<Route path="/:id/profile" name='Profile' component={Profile} onEnter={checkRoles}/>
<Route path=":loginId" name="NT" component={TransparentIndexPage} onEnter={checkRoles}>
<Switch>
<Route path="post" name='Post' component={Post} />
<Route path="search" component={Search} />
<Route path="settings" component={Settings} />
</Switch>
</Route>
<Route path="*" component={NotFound}/>
</Switch>
</div>
</ConnectedRouter>
</Provider>
)
export default routes
history.js
import createBrowserHistory from 'history/createBrowserHistory';
import createMemoryHistory from 'history/createMemoryHistory';
export default process.env.BROWSER ? createBrowserHistory() : createMemoryHistory();
store.js
/*eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }]*/
import {Map} from 'immutable';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
import browserStorage from './browserStorage';
import { routerReducer, routerMiddleware, push } from 'react-router-redux';
import {default as history} from './history';
const middlewareHistory = routerMiddleware(history);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
//sessionmiddleware is not required as we are using jsonwebtoken
const sessionMiddleware = _store => next => action => {
let result = next(action)
switch(action.type) {
case 'LOGIN_SUCCESS':
browserStorage.set({
realUser: {
loginId: action.user.id,
token: action.token
},
currentUser: {
loginId: action.user.id,
token: action.token
}
})
break;
case 'USER_LOGOUT_SUCCESS':
localStorage.clear()
break;
default:
break;
}
return result
}
const store = createStore(
reducer,
Map(),
composeEnhancers(
applyMiddleware(
sessionMiddleware,
middlewareHistory,
thunk
),
window && window.devToolsExtension ? window.devToolsExtension() : f => f
)
);
export default store;
Did you try to put a "/" before your route names like :
<Route path="/contact" component={Contact} />

Routing in Meteor with React & React-Router

I want to make a second private page like links in MeteorJS with react, called landing, create the Landing.js component and import it into the routes file but at the time of going to the route in the browser "http: // localhost: 3000/landing "sends me to the NotFound page, which may be wrong? I would be grateful for the help
'../imports/routes/routes';
import React from 'react';
import Meteor from 'meteor/meteor';
import { Router, Route, browserHistory } from 'react-router';
import Vitae from '../ui/Vitae';
import Logeo from '../ui/Logeo';
import Registro from '../ui/Registro';
import NoEncontrado from '../ui/NoEncontrado';
import Landing from '../ui/Landing';
// flecha tracker
Tracker.autorun(() => {
const paginasUnautenticadas = ['/', '/registro'];
const paginasAutenticadas = ['/vitae', '/landing'];
const enPaginaPublica = () => {
if(Meteor.userId()) {
browserHistory.replace('/vitae');
}
};
const enPaginaPrivada = () => {
if(!Meteor.userId()) {
browserHistory.replace('/');
}
};
export const cambioAutenticacion = (estaAutenticado) => {
const pathname = browserHistory.getCurrentLocation().pathname;
const esPaginaUnautenticada = paginasUnautenticadas.includes(pathname);
const esPaginaAutenticada = paginasAutenticadas.includes(pathname);
if(esPaginaUnautenticada && estaAutenticado) {
browserHistory.replace('/vitae');
} else if (esPaginaAutenticada && !estaAutenticado) {
browserHistory.replace('/');
}
};
export const routes = (
<Router history={browserHistory}>
<Route path="/" component={Logeo}/>
<Route path="/vitae" component={Vitae}/>
<Route path="/registro" component={Registro}/>
<Route path="*" component={NoEncontrado}/>
<Route path="/landing" component={Landing}/>
</Router>
);
});
and my component Landing.js
import React from 'react';
export default class Landing extends React.Component {
render() {
return(
<div>
<h3>Landing Page</h3>
</div>
);
};
}
You have your Landing route after your wildcard "Not Found" route
<Route path="*" component={NoEncontrado}/>
<Route path="/landing" component={Landing}/>
I'm pretty sure that if you switch these two over, it will work as expected :)
<Route path="/landing" component={Landing}/>
<Route path="*" component={NoEncontrado}/>

Categories