I have tried to disable back button in browser but it just reloading the page to the path="/"
How can I disable back button of the browser, so when user clicks it nothing happens??
export default class Main extends Component {
componentDidMount() {
const history = createBrowserHistory();
history.listen((newLocation, action) => {
if (action === "POP") {
history.go(0);
}
});
}
render() {
return (
<div>
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/main/component1" component={Component1} />
<Route path="/main/component2" component={Component2} />
<Route path="/main/component3" component={Component3} />
</Switch>
</Router>
</div>
);
}
}
You can do it using react-router-dom
import { useHistory } from "react-router-dom";
let history = useHistory();
history.replace("/login");
Related
I am new to localStorage and React Router, and my goal is:
Redirect user to the "/dashboard" when he is logged in, and Redirect back to '/home' when he is logged out. Also, of course, not allowing him to go to the 'dashboard' if he is not logged in. For some reason my code in App.js not working:
function App() {
let userLogged;
useEffect(() => {
function checkUserData() {
userLogged = localStorage.getItem("userLogged");
}
window.addEventListener("storage", checkUserData);
return () => {
window.removeEventListener("storage", checkUserData);
};
}, []);
return (
<div className="App">
<React.StrictMode>
<Router>
<Routes>
<Route path="/" element={<Home />} />
{userLogged ? (
<Route path={"/dashboard"} element={<Dashboard />} />
) : (
<Route path={"/home"} element={<Home />} />
)}
</Routes>
</Router>
</React.StrictMode>
</div>
);
}
export default App;
I set it in the home and dashboard pages by localStorage.setItem('userLogged', false) and localStorage.setItem('userLogged', true)
You can only listen to changes in localStorage from other window/browser contexts, not from within the same browser/window context. Here it's expected the window knows its own state. In this case, you actually need some React state.
Convert the userLogged to a React state variable and use a useEffect hook to initialize and persist the userLogged state to/from localStorage. Instead of conditionally rendering Route components, create a wrapper component to read the userLogged value from localStorage and conditionally render an Outlet for nested/wrapped routes or a Navigate component to redirect to your auth route to login.
Example:
import { Navigate, Outlet, useLocation } from 'react-router-dom';
const AuthWrapper = () => {
const location = useLocation(); // current location
const userLogged = JSON.parse(localStorage.getItem("userLogged"));
return userLogged
? <Outlet />
: (
<Navigate
to="/"
replace
state={{ from: location }} // <-- pass location in route state
/>
);
};
...
function App() {
const [userLogged, setUserLogged] = useState(
JSON.parse(localStorage.getItem("userLogged"))
);
useEffect(() => {
localStorage.setItem("userLogged", JSON.stringify(userLogged));
}, [userLogged]);
const logIn = () => setUserLogged(true);
const logOut = () => setUserLogged(false);
return (
<div className="App">
<React.StrictMode>
<Router>
<Routes>
<Route path="/" element={<Home logIn={logIn} />} />
<Route path={"/home"} element={<Home logIn={logIn} />} />
<Route element={<AuthWrapper />}>
<Route path={"/dashboard"} element={<Dashboard />} />
</Route>
</Routes>
</Router>
</React.StrictMode>
</div>
);
}
export default App;
Home
import { useLocation, useNavigate } from 'react-router-dom';
const Home = ({ logIn }) => {
const { state } = useLocation();
const navigate = useNavigate();
const loginHandler = () => {
// authentication logic
if (/* success */) {
const { from } = state || {};
// callback to update state
logIn();
// redirect back to protected route being accessed
navigate(from.pathname, { replace: true });
}
};
...
};
You can render both route and use Navigate component to redirect. Like this -
// [...]
<Route path={"/dashboard"} element={<Dashboard />} />
<Route path={"/home"} element={<Home />} />
{
userLogged ?
<Navigate to="/dashboard" /> :
<Navigate to="/home" />
}
// other routes
Whenever you logout, you need to manually redirect to the desired page using useNavigate hook.
I am trying to limit user going back to the previous page,
It works but I can see previous page for millisecond and then current page reloads with
all API requests.
How can I prevent that behaviour?
import React, { Component } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { createBrowserHistory } from "history";
export default class Main extends Component {
componentDidMount() {
// const history = useHistory();
const history = createBrowserHistory();
history.listen((newLocation, action) => {
if (action === "PUSH") {
if (
newLocation.pathname !== this.currentPathname ||
newLocation.search !== this.currentSearch
) {
// Save new location
this.currentPathname = newLocation.pathname;
this.currentSearch = newLocation.search;
// Clone location object and push it to history
history.push({
pathname: newLocation.pathname,
search: newLocation.search,
});
}
} else {
// Send user back if they try to navigate back
history.go(1);
}
});
}
render() {
return (
<div>
<BrowserRouter>
<Switch>
<Route exact path="/" component={Page1} />
<Route path="/main/page2" component={Page2} />
<Route path="/main/page3" component={Page3} />
<Route path="/main/page4" component={Page4} />
<Route path="/main/page5" component={Page5} />
</Switch>
</BrowserRouter>
</div>
);
}
}
Try set the routes replace:
<Route path="/main/page2" component={Page2} replace={true}/>
<Route path="/main/page3" component={Page3} replace={true}/>
<Route path="/main/page4" component={Page4} replace={true}/>
<Route path="/main/page5" component={Page5} replace={true}/>
When a user completes a booking process and navigate to the confirmed details view. I need the URL to change. This is proving to be difficult to work around as the routing in done through MemoryRouter which can neither read, nor write to the URL. I need to break one of the views out and have the browser navigate to this new view.
I have tried breaking out from one router and creating a second that would return based on the original URL, then tried the very hacky window.location and direct the url to the new router.
import React from 'react';
import { MemoryRouter, Route, Switch } from 'react-router-dom';
import {
Page,
StartScreen,
StoreSearch,
ServiceSelector,
StoreSelector,
OptionSelector,
AppointmentForm,
AppointmentDetails,
ConfirmationScreen,
ErrorScreen,
} from 'components';
import { WithPageTitle, ScrollToTop } from 'containers';
import { services } from 'utilities';
const NewAppRouter = () => {
return (
<MemoryRouter>
<ScrollToTop>
<Switch>
<Route exact path="/" component={StartScreen} />
<WithPageTitle>
{pageTitle => (
<Page pageTitle={pageTitle}>
<Route path="/zip" component={StoreSearch} />
<Route path="/services" component={() => ServiceSelector({
services: services.services,
withBackButton: true,
backTo: "/zip"
})} />
<Route path="/stores" component={StoreSelector} />
<Route path="/options" component={OptionSelector} />
<Route path="/form" component={AppointmentForm} />
<Route path="/details" component={AppointmentDetails} />
{/* <Route path="/confirmation" component={ConfirmationScreen} /> */}
<Route path="/error" component={ErrorScreen} />
</Page>
)}
</WithPageTitle>
</Switch>
</ScrollToTop>
</MemoryRouter>
)
}
const AppRouter = () => {
if(window.location.href="http://localhost:9998"){
return (
<NewAppRouter />
)
} else if (window.location.href="http://localhost:9998/confirmation") {
return (
<ConfirmRouter />
)
} else {
return console.error('Route Not Found')
}
}
export default AppRouter;
The logic seems simple, though I've tried a half-dozen permutations to see if anything changes. I have no idea why react-router is behaving this way:
import React from 'react'
import { View, Text } from 'react-native'
import { observer, inject } from 'mobx-react'
import { NativeRouter, Link, Route, Redirect, Switch } from 'react-router-native'
import Welcome from './welcome'
import Tutorial from './tutorial'
import Plants from './plants'
#inject('store')
#observer
class Main extends React.Component {
render() {
const newUser = true //this.props.store.plants.length === 0
const home = newUser ? '/welcome' : '/plants'
return (
<Switch>
<Route path='/plants' component={Plants} />
<Route path='/tutorial' component={Tutorial} />
<Route path='/welcome' component={Welcome} />
<Redirect to={home} />
<Route path='/' component={Welcome} />
</Switch>
)
}
}
export default Main
The final 'welcome' should be unnecessary, but I've put it there to test: if I remove the then welcome does appear, so it's clearly the that's causing a blank page to render.
This is the render() method of the top-level component:
return (
<Provider store={store}>
<NativeRouter>
<Main />
</NativeRouter>
</Provider>
)
This is based on the example at https://reacttraining.com/react-router/native/guides/philosophy which shows a Switch, Route, and Redirect all being used without an enclosing Router:
const App = () => (
<AppLayout>
<Route path="/invoices" component={Invoices}/>
</AppLayout>
)
const Invoices = () => (
<Layout>
{/* always show the nav */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// small screen has no redirect
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// large screen does!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
Use the NativeRouter as the topmost component in your Main component and it will work as expected.
#inject('store')
#observer
class Main extends React.Component {
render() {
const newUser = true //this.props.store.plants.length === 0
const home = newUser ? '/welcome' : '/plants'
return (
<NativeRouter>
<Switch>
<Route path='/plants' component={Plants} />
<Route path='/tutorial' component={Tutorial} />
<Route path='/welcome' component={Welcome} />
<Redirect to={home} />
</Switch>
</NativeRouter>
)
}
}
How to acces query parameter in container, using react-router and react-kompose:
I am tying like this but it is not working:
routes.js
...
Meteor.startup(() => {
render(
<Router history={ browserHistory }>
<Route path="/" component={ App }>
<IndexRoute name="index" component={ Index } onEnter={ requireAuth } />
<Route name="documents" path="/documents" component={ Documents } onEnter={ requireAuth } />
<Route name="list-projects" path="/list-projects" component={ Projects } onEnter={ requireAuth } />
<Route name="add-project" path="/add-project" component={ AddProjectPage } onEnter={ requireAuth } />
<Route name="project-detail" path="/project/:projectId" component={ProjectDetailPage} onEnter={ requireAuth } />
<Route name="login" path="/login" component={ Login } />
<Route name="recover-password" path="/recover-password" component={ RecoverPassword } />
<Route name="reset-password" path="/reset-password/:token" component={ ResetPassword } />
<Route name="signup" path="/signup" component={ Signup } />
<Route path="*" component={ NotFound } />
</Route>
</Router>,
document.getElementById('react-root')
);
});
container1.js
import { composeWithTracker } from 'react-komposer';
import { Projects } from '../../api/projects/projects.js';
import { ProjectDetail } from '../components/project-detail.js';
import { Loading } from '../components/loading.js';
import { Meteor } from 'meteor/meteor';
const composer = (params, onData) => {
const subscription = Meteor.subscribe('projects');
if (subscription.ready()) {
const project = Projects.find({_id:params._id}).fetch();
onData(null, { project});
}
};
export default composeWithTracker(composer, Loading)(ProjectDetail);
You probably figured it out by now, but you can get parameters inside props of the container like so, using your example just a little modified:
const composer = (props, onData) => {
const subscription = Meteor.subscribe('projects');
if (subscription.ready()) {
const project = Projects.find({_id:props.params.projectId}).fetch();
onData(null, { project});
}
};
export default composeWithTracker(composer, Loading)(ProjectDetail);
React Router will pass everything to its child components. props is an object, so you can access the params just by getting the value of props.params. You can access many more things inside your container depending on your needs.