React-Router onChange hook - javascript

I am having issues getting the onChange hook in react-router to work properly. Here is my routes file:
import React from 'react';
import { Router, Route, browserHistory } from 'react-router';
import TestOne from './Pages/testone';
import TestTwo from './Pages/testtwo';
function logUpdate() {
console.log('Current URL: ' + window.location.pathname);
}
const Routes = (
<Router history={browserHistory}>
{/* App Routes */}
<Route path="/" component={App} lang={lang}>
<Route path="/testone" component={TestOne} onUpdate={logUpdate} />
<Route path="/testtwo" component={TestTwo} onUpdate={logUpdate} />
</Route>
</Router>);
export default Routes;
My understanding is, that the function logUpdate will be triggered on each state change. However, it is only triggered when I reload the corresponding page via F5.
My menu is using simple Links e.g.:
<div>
<Link to="/testone">Test One</Link>
<Link to="/testtwo">Test Two</Link>
</div>
What am I doing wrong?

onUpdate needs to be declared on the Router instance not Routes. Although, Routes can declare onChange and onEnter hooks - it's probably what you were looking for.

I'm using react-router ^2.4.0 and onUpdate did not work for me. I have instead used onChange on my base Route component.
const Routes = (
<Router history={browserHistory}>
{/* App Routes */}
<Route path="/" component={App} lang={lang} onChange={logUpdate}>
<Route path="/testone" component={TestOne} />
<Route path="/testtwo" component={TestTwo} />
</Route>
</Router>);

Related

Why my components don't display on my React page?

So I'm learning React and building an app with multiple pages, I made a Routes file which looks like this:
import 'swiper/swiper.min.css';
import React from "react";
import { Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import Catalog from "../pages/Catalog";
import Detail from "../pages/Detail";
const Router = () => {
return (
<Routes>
<Route
path='/:category/search/:keyword'
component={Catalog}
/>
<Route
path='/:category/:id'
component={Detail}
/>
<Route
path='/:category'
component={Catalog}
/>
<Route
path='/'
exact
component={Home}
/>
</Routes>
);
}
And App.js looks like this:
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Header from './components/header/Header';
import Footer from './components/footer/Footer';
import Router from './config/Router';
function App() {
return (
<BrowserRouter>
<Routes>
<Route render={props =>{
<>
<Header {...props}/>
<Router/>
<Footer/>
</>
}}/>
</Routes>
</BrowserRouter>
);
}
export default App;
As you see, I have a browser router and Route which passes props to a component(as I understood) but for some reason the components don't display on the page(original components just have with their name inside of them, but they don't display in App.js).
And my console also says:
No routes matched location "/"
In routes.jsx file. I'm guessing it should lead to main page, but for some reason the route doesn't match and components in App.js don't display.
In Version 6.0.0 there is not any component prop in Route. It has been changed to element. So you need to change your Router to :
const Router = () => {
return (
<Routes>
<Route
path='/:category/search/:keyword'
element={Catalog}
/>
<Route
path='/:category/:id'
element={Detail}
/>
<Route
path='/:category'
element={Catalog}
/>
<Route
path='/'
exact
element={Home}
/>
</Routes>
);
}
As you've said you're using react-router-dom 6.0.2, and it seems that the tutorial you are following is for the older version (5?). There were some breaking changes in version 6.
You need to change your Router component to use element instead of component:
const Router = () => {
return (
<Routes>
<Route path="/:category/search/:keyword" element={<Catalog />} />
<Route path="/:category/:id" element={<Detail />} />
<Route path="/:category" element={<Catalog />} />
<Route path="/" exact element={<Home />} />
</Routes>
);
};
and also your App component seems to be getting in the way with the nested route.
I think it can be simplified to:
function App() {
return (
<BrowserRouter>
<>
<Header />
<Router />
<Footer />
</>
</BrowserRouter>
);
}
You can see a working demo on stackblitz

React js: React router not redirecting when button is being clicked

I have been trying to solve this problem for couple days and still couldnt get it to work. How does redirecting not work here? I keep getting the "TypeError: history is undefined". Also when the button is being clicked, it stays at the same url. What am I doing wrong?
import React from 'react';
import Nav from './Nav';
import About from './About';
import Service from './Service';
import Home from './Home';
import {
BrowserRouter as Router,
Switch,
Route,
useHistory,
} from 'react-router-dom';
function App() {
const history = useHistory();
function handleSub() {
// console.log('clicked');
history.push('/about');
}
return (
<div className='App'>
<button onClick={handleSub}>submit</button>
<Router>
<Nav />
<Switch>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
<Route path='/service' component={Service} />
</Switch>
</Router>
</div>
);
}
export default App;
Edit: implemented the answers and still having trouble.
Suggestion 1: place the button inside the router: url switches for links or button is clicked but pages for the links not when the button is clicked.
function App() {
const history = useHistory();
function handleSub() {
// console.log('clicked');
history.push(`/about`);
}
return (
<Router>
<div className='App'>
<Nav />
<button onClick={handleSub}>submit</button>
<Switch>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
<Route path='/service' component={Service} />
</Switch>
</div>
</Router>
);
}
Suggestion number two: url swtich for all the links and button but the page never loads.
function App() {
const history = useHistory();
function handleSub() {
// console.log('clicked');
history.push(`/about`);
}
return (
<Router>
<div className='App'>
<Nav />
<Switch>
<button onClick={handleSub}>submit</button>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
<Route path='/service' component={Service} />
</Switch>
</div>
</Router>
);
}
Simply because you are calling useHistory hook outside of React Router Switch Component, meaning you should use this hook in all Switch child components, otherwise the history object is undefined. The best way for my opinion is to move the button to the Home Component, then surly it will work.
I hope this answer helped you.
If you use it like this
<Button color='primary' onClick={handleSub('/about')}>Submit</Button>
and in your method do it like this.
const handleSub = (path) => async (e) => {
history.push(path)}
I hope it will solve your issue.
It's not the placement of the button which is the issue, but as originally mentioned, the placement of useHistory. The history hook is expecting context provided by the Router component - if you are familiar with useContext, it's exactly the same here. You must be calling useHistory inside a component which is a child of Router.
I'll mock an index.js file to demonstrate:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './App';
const rootHtml = (
<Router>
<App />
</Router>
);
ReactDOM.render(rootHtml, document.getElementById('root'));
Remove the original router in the App component. Now this top level router has the App component in scope and can provide it with history context for useHistory to use.

React Router always renders NotFound default component

I've been searching all over the internet for solutions but alas I come here for some help. The problem is that the URL changes but the respective component in Route don't render, instead of that, the NotFoundPage is rendered.
Here's the App.js Router code:
<Router history={history}>
<Switch>
<PrivateRoute exact path="/" component={HomePage} />
<Route path="/login" component={LoginPage} />
<Route path="/register" component={RegisterPage} />
<Route component={NotFoundPage}/>
</Switch>
</Router>
Here's PrivateRoute code:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
localStorage.getItem('user')
? <Component {...props} />
: <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
)} />
)
The problem I'm facing is the when I go to / the NotFoundPage is rendered instead of LoginPage but the URL correctly redirects and changes to /login. But when I refresh, the LoginPage is rendered. Similarly, when I click on link that takes to /register from LoginPage the URL changes but RegisterPage doesn't render it's the same NotFoundPage.
During contribution on github we figured solution.
This weird behavior flows from using custom history.
const customHistory = createBrowserHistory();
ReactDOM.render(<Router history={customHistory} />, node);
When it is used then somehow router don't react to changes in a location path.
Until you don't add:
<Switch location={window.location}>
According to the docs the location in switch by default is set to window. location but for some reason when you don't use BroswerRouter or StackRouter but just Router this starts working when we directly set location.

React-router-dom Router not working. Jumps to 404 page only

I am working with react router on a small project. I initially had my AppRouter working with BrowserRouter and everything works fine. But I had to switch to Router so I could add my own history object. With Router my page navigations do not work, instead it jumps straight to my 404 page. Any suggestions on what I am doing wrong will be appreciated.
import React from "react";
import AddExpensePage from "../components/AddExpensePage";
import EditExpensePage from "../components/EditExpensePage";
import ExpenseDashboardPage from "../components/ExpenseDashboard";
import Header from "../components/Header";
import HelpPage from "../components/HelpPage";
import NotFoundPage from "../components/NotFoundPage";
import { createBrowserHistory } from "history";
import LoginPage from "../components/LoginPage";
import { Switch, BrowserRouter, Route, Router } from "react-router-dom";
export const history = createBrowserHistory();
const AppRouter = () => (
<Router history={history}>
<div>
<Header />
<Switch>
<Route path="/" component={LoginPage} exact={true} />
<Route path="/dashboard" component={ExpenseDashboardPage} />
<Route path="/create" component={AddExpensePage} />
<Route path="/edit/:id" component={EditExpensePage} />
<Route path="/help" component={HelpPage} />
<Route component={NotFoundPage} />
</Switch>
</div>
</Router>
);
export default AppRouter;
I would suggest that you keep using BrowserRouter. React Hooks now make using history possible despite the type of Router you're using.
From ReactRouter documentation, useHistory is there to your rescue:
The useHistory hook gives you access to the history instance that you may use to navigate.
To access the history object anywhere in your app inside the Routed Components, you can do the following inside of that component:
let history = useHistory();
Then you have access to history.push() and other methods you wish to call to fiddle with history.
Conclusion:
Don't switch to <Router>, keep using <BrowserRouter> and use hooks to access history using useHistory.
You have to wrapp your routes with BrowserRouter component, for example:
const AppRouter = () => (
<Router history={history}>
<div>
<Header />
<BrowserRouter>
<Switch>
<Route path="/" component={LoginPage} exact={true} />
<Route path="/dashboard" component={ExpenseDashboardPage} />
<Route path="/create" component={AddExpensePage} />
<Route path="/edit/:id" component={EditExpensePage} />
<Route path="/help" component={HelpPage} />
<Route component={NotFoundPage} />
</Switch>
</BrowserRouter>
</div>
</Router>
);

Matched route not changing on routing hash change

I'm using react-router-dom with react-router-redux and history to manage routing for my app. I'm also using hash history for support on legacy browsers. Below are my route components:
<Switch>
<Route exact path={'/'} component={...} />
<Route path={'/a'} component={...} />
<Route path={'/b'} component={...} />
</Switch>
My app lands at the location: http://something.com/index.html#/, and correctly is routed to the first Route component. However, when using dispatch(push('/a')) in a thunk action creator to attempt to programatically switch routes, I'm finding that the proper route is not being matched.
I'm having a difficult time debugging this... any ideas? I'm thinking it perhaps has to do with the fact that my window.location.pathname is /index.html.
Switch receive a location prop, or must be wrapped with Router component. You can find more information at https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md#children-node/
If a location prop is given to the , it will override the location prop on the matching child element.
So try one of these ways:
class Example extends Component {
render() {
return (
<Switch location={this.props.location}>
<Route exact path={'/'} component={...} />
<Route path={'/a'} component={...} />
<Route path={'/b'} component={...} />
</Switch>
);
}
// location is react-router-redux reducer
export default connect(state => ({location: state.location}))(Example);
Or, another way you can do, it's wrap your Switch component with Router component (I pasted code from one of my project):
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import configureStore from './store/configureStore';
const history = createHistory();
const store = configureStore(history);
// We wrap Switch component with ConnectedRouter:
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<Route exact path={'/'} component={...} />
<Route path={'/a'} component={...} />
<Route path={'/b'} component={...} />
</Switch>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
);
More information about Router components you can find here: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Router.md

Categories

Resources