js and facing hard time with react router dom.
I want to change the route when I click a button in my navigation drawer component.
This is my App.js component.
import { Router, Route, Switch } from "react-router-dom";
function App() {
const classes = useStyles();
return (
<Provider store={store}>
<div className={classes.root}>
<NavBar />
<Drawer />
<Router history={history}>
<Switch>
<Route path="/" exact component={Login}></Route>
<Route path="/country" exact component={Country}></Route>
<Route path="/user-create" exact component={User}></Route>
<Route path="/countries" exact component={ListView}></Route>
<Route component={NotFound}></Route>
</Switch>
</Router>
</div>
</Provider>
);
}
And here I pass history prop to the Router component.
History.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Drawer.js
import history from '../../history'
const onRoute = (path) => {
history.push("/user-create");
// props.toggleDrawer();
}
In Drawer.js it is always route to the NotFoundComponet.
Why could this happen?
In your NavBar and Drawer components include the below withRouter or the alt useHistory hook to get access to the history prop.
import { withRouter } from 'react-router'
...
this.props.history.push('/user-create')
...
export default withRouter(...
I'd put <NavBar /> and <Drawer /> inside <Router>, and do
const { push } = useHistory() // from react-router-dom
inside them, to get a reference of the push API
Related
in Switch Route Routing time it's working, but now latest new Routes, Route it not working custom route
I have wrapped the navbar page and home page in HomeLayoutHOC
can anyone help me :) how to do this latest version I try but so many things. no result for this
I want 'HomeLayoutHOC " route instead of "Route"
->client\src\App.JSX :
//HOC
import HomeLayoutHOC from "./HOC/Home.Hoc";
import { Route, Routes } from "react-router-dom";
//Component
import Temp from "./Components/temp";
function App() {
return (
<>
<Routes>
<HomeLayoutHOC path="/" exact element={Temp} /> // <--- I want this to work!
// <Route path="/" element={<Temp />} /> // <-- this working fine
</Routes>
</>
);
}
export default App;
result 👇
screenshot!
->client\src\index.jsx :
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import "./index.CSS";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
-> client\src\HOC\Home.Hoc.jsx
import React from "react";
import { Route } from "react-router-dom";
// Layout
import HomeLayout from "../Layout/Home.Layout";
const HomeLayoutHOC = ({ component: Component, ...rest }) => {
return (
<>
<Route
{...rest}
component={(props) => (
<HomeLayout>
<Component {...props} />
</HomeLayout>
)}
/>
</>
);
};
export default HomeLayoutHOC;
->client\src\Layout\Home.Layout.jsx
import React from "react";
// Components
import Navbar from "../Components/Navbar";
const HomeLayout = (props) => {
return (
<>
<Navbar />
<div className="container mx-auto px-4 lg:px-20 ">{props.children}</div>
</>
);
};
export default HomeLayout;
please give me the possible suggestion for the latest router dom (Routes, Route)
wrapping/composing
How can I spread routeProps to make them available to your rendered Component the latest router dom (Routes, Route)
react-router-dom#6 removed the need, and compatibility, for custom route components. It's an invariant violation to render anything other than a Route or React.Fragment component in the Routes component. Custom route components are replaced with the use of either wrapper components on individual routes wrapping the element being rendered, or by layout route components that can wrap any number of nested Route components.
Wrapper components render the children prop
<Route
path="/"
element={(
<Wrapper>
<Componenet />
</Wrapper>
)}
>
Layout Route components render an Outlet component for nested routes to render their element prop into.
<Route element={<Layout />}>
<Route path="/" element={<Componenet />} />
</Route>
You are asking for the Layout Route version since it seems you want to render the Navbar component as part of a greater layout.
HomeLayout
import React from "react";
import { Outlet } from "react-router-dom";
import Navbar from "../Components/Navbar";
const HomeLayout = () => {
return (
<>
<Navbar />
<div className="container mx-auto px-4 lg:px-20 ">
<Outlet />
</div>
</>
);
};
export default HomeLayout;
App
Render HomeLayout on a pathless route as a Layout Route. The nested Route components render their content into the outlet.
import { Route, Routes } from "react-router-dom";
import HomeLayout from "./path/to/HomeLayout";
import Temp from "./Components/temp";
function App() {
return (
<Routes>
<Route element={<HomeLayout />}>
<Route path="/" element={<Temp />} />
... other routes to render with Home layout and Navbar ...
</Route>
... other routes to render without Home layout and Navbar ...
</Routes>
);
}
An important aspect you should notice here is that RRDv6 removed route props, all the old "props" are now only accessible via React hooks in the routed component, i.e. useNavigate, useLocation, useParams, etc. If you are still using React class-based components they won't be able to use React hooks, and since RRDv6 also no longer exports the withRouter Higher Order Component, well, you will need to roll your own. See What happened to withRouter? I need it! for details.
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.
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
I want to pass down the history prop down from the App to the Navigation component.
When I try to do so, I get the following error message:
Failed prop type: The prop history is marked as required in Navigation, but its value is undefined.
How can I resolve this issue?
App.js:
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
const App = props => (
<Router>
<MainLayout {...props}>
<Switch>
<Route exact name="index" path="/" component={Index}/>
<Route component={NotFound}/>
</Switch>
</MainLayout>
</Router>
);
MainLayout.js:
import React from "react";
import PropTypes from "prop-types";
import Navigation from "../../components/Navigation/Navigation";
const MainLayout = props => {
const { children, authenticated, history } = props;
return (
<div>
<Navigation authenticated={authenticated} history={history} />
{children}
</div>
);
};
MainLayout.PropTypes = {
children: PropTypes.node.isRequired,
authenticated: PropTypes.bool.isRequired,
history: PropTypes.object.isRequired,
};
export default MainLayout;
SOLUTION #1:
If you simply convert <MainLayout /> to a <Route /> that renders you will have access to the history object.
<Route render={(props) =>
<MainLayout {...props}>
<Switch>
<Route exact name="index" path="/" component={Index}/>
<Route component={NotFound}/>
</Switch>
</MainLayout>
}/>
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Route.js
<App /> does not have access to history as a prop so this will never do what you are wanting <MainLayout {...props}>
SOLUTION #2
You can also reference the history object as a single exported module in your app and refer to that both React router and any other compopent / javascript file in your app.
import { Router, Switch, Route } from 'react-router-dom';
import history from './history';
const App = props => (
<Router history={history}>
<MainLayout history={history} {...props}>
<Switch>
<Route exact name="index" path="/" component={Index}/>
<Route component={NotFound}/>
</Switch>
</MainLayout>
</Router>
);
(history.js)
import createBrowserHistory from 'history/createBrowserHistory';
export default createBrowserHistory();
https://www.npmjs.com/package/history
For react-router-dom v4
In order to get Router component's prop I used the withRouter, I guess the below change should work,
export default wihtRouter(MainLayout);
This should enable the usage of props.history in MainLayout
Next to the Web Tips Picture
https://i.stack.imgur.com/A2OGn.png
Warning: Hash history cannot PUSH the same path; a new entry will not be added to the history stack
Tips error , This is when I click on the link again
Next to Picture is React-Router File Code...
https://i.stack.imgur.com/WgqqN.png
import { HashRouter, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import View from './containers';
import configureStore from './store/configureStore';
const store = configureStore();
const AppRouter = () => (
<Provider store={store}>
<HashRouter>
<View.App.Container>
<Route path='/' exact={true} component={View.App.Dashboard} />
<Route path='/Todo' component={View.Todo.Container} />
<Route path='/News' render={() => (
<View.News.Container>
<Route path='/News/List' render={() => (
<h2>News List Show</h2>
)} />
</View.News.Container>
)} />
</View.App.Container>
</HashRouter>
</Provider>
);
export default AppRouter;
If you use the component Link for navigation, you may want to set the prop replace on it.
https://reacttraining.com/react-router/web/api/Link/replace-bool