Routes not working in react-router v4 - reactjs

I am trying to setup react router in my react application. The versions I am using are
"react-dom": "^16.2.0",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9",
"redux": "^3.7.2",
I am using reacr-router-redux for this appilication.
Application Flow: I have two pages in my application: Login Page and App Page. Landing page should be Login Page and when I click on Login button then it should take me to the App Page. There are three sections in App Page: Header, Sidebar and Content Section. Content section is dynamic and renders two different layouts depending on which link is clicked in sidebar. Only one component at a time can be rendered in content section.
Problem: I have defined routes. I get landed to LoginPage correctly. When I click to Login, I get navigated to app page also correctly. But when I click a link on sidebar, all the components (sidebar, header and content section) disappears.
My code
main index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
import Root from './Container/Root'
import {Provider} from 'react-redux'
import {ConnectedRouter} from 'react-router-redux'
import store, {history} from './store'
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<Root />
</div>
</ConnectedRouter>
</Provider>,
document.getElementById('root'));
registerServiceWorker();
Root/index.js
import React, {Component, PropTypes } from 'react'
import { Provider } from 'react-redux'
import LoginPage from '../../Pages/LoginPage'
import App from '../../Container/App/App'
import { Route, Link } from 'react-router-dom'
const styles = {
container: {
height: '100vh',
width : '100vw',
position: 'relative'
}
}
class Root extends Component {
render () {
return (
<div>
<div>
<main>
<Route exact path="/" component={LoginPage} />
<Route exact path="/app" component={App} />
</main>
</div>
</div>
)
}
}
export default Root
App.js (here's where the problem is)
import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom'
import Sidebar from '../../Components/Sidebar'
import TriviaPanel from '../../Components/TriviaPanel'
import Header from '../../Components/Header'
import ImagePanel from '../../Components/ImagePanel'
import LoginPage from '../../Pages/LoginPage'
class App extends Component {
render() {
return (
<div>
{/*<LoginPage/> */}
<Header/>
<Sidebar/>
<div>
<main>
<Route component={TriviaPanel} />
<Route exact path="/trivia" component={TriviaPanel} />
<Route exact path="/image" component={ImagePanel} />
</main>
</div>
</div>
);
}
}
export default App;
MenuPanel/index.js (This is the sidebar I change the content component from)
import React, { Component } from 'react';
import { push } from 'react-router-redux'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
const styles = require('./sidebar.css');
class MenuPanel extends Component {
constructor(){
super();
this.state = {
activePanel: "trivia"
}
}
toImagePage(){
this.setState({activePanel:"image"},()=>{
this.props.toImagePage()
})
}
toTriviaPage(){
this.setState({activePanel:"trivia"},()=>{
this.props.toTriviaPage()
})
}
render() {
return (
<div>
<div className="navbar-side">
<div className="tessact-logo"></div>
<div className={`navbar-item ${this.state.activePanel == "trivia"? "active":""}`} onClick={() => this.toTriviaPage()}>
<a className="navbar-item-link"><span className="fa fa-comment"></span> TRIVIA</a>
</div>
<div className={`navbar-item ${this.state.activePanel == "image"? "active":""}`} onClick={() => this.toImagePage()}>
<a className="navbar-item-link"><span className="fa fa-picture-o"></span> IMAGES</a>
<div className="navbar-item-inside">
<a className="navbar-item-inside-link">PERSONSS</a>
<a className="navbar-item-inside-link">BRANDS</a>
<a className="navbar-item-inside-link">OBJECTS</a>
</div>
</div>
<div className="navbar-item">
<a className="navbar-item-link"><span className="fa fa-tags"></span> KEYWORDS</a>
</div>
</div>
</div>
);
}
}
const mapDispatchToProps = dispatch => bindActionCreators({
toTriviaPage: () => push('/trivia'),
toImagePage: () => push('/image')
}, dispatch)
export default connect(
null,
mapDispatchToProps
)(MenuPanel)
I tried this also
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
{/*<LoginPage/> */}
<Header/>
<Sidebar/>
<switch>
<Route exact path="/trivia" component={TriviaPanel}/>
<Route exact path="/image" component={ImagePanel}/>
</switch>
<Route component={TriviaPanel}/>
</div>
</BrowserRouter>
);
}
}
export default App;
What is wrong in what I am doing? In previous versions of router it was pretty simple to define child routes and everything. I am new to this router v4.

The problem is with your routes. i faced the same problem recently. so here is your problem. you clicked the link for 'app'. the url becomes
http://localhost/app
now all routes defined inisde the app component must be /app/link.
therefore
<link to='/trivia'>Trivia</link> //example. use whatever metohd you are using to route instead. use the given url
<Route exact path="/trivia" component={TriviaPanel}/>
becomes
<link to='/app/trivia'>Trivia</link>
<Route exact path="/app/trivia" component={TriviaPanel}/>
this tells react that it needs to go into the app component and then search for the route tag in its jsx to render.

Related

React router displays component only on refresh

I want to create a simple react router example and have added my code below. The components gets displayed on refresh but the links doesn't seem to work. I have kept my links in 'Header.js' file and the components inside a folder called 'functional'. Can someone help me with this, am new to this and appreciate all the help.
routes.js
import React, { Component } from 'react';
import Component1 from './functional/component1';
import Component2 from './functional/component2';
import Component3 from './functional/component3';
import Container1 from './container/container1';
import Header from './container/header';
import history from './utils/history';
import { Router, Route, Switch } from 'react-router';
class Routes extends Component {
render() {
return (
<div>
<Router history={history}>
<div>
<Header />
<Switch>
<Route exact path = "/" component={Container1} />
<Route path="/component1" render={() => <Component1/>} />
<Route path="/component2" component={Component2} />
<Route path="/component3" component={Component3} />
</Switch>
</div>
</Router>
</div>
);
}
}
export default Routes;
header.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class Header extends Component {
render () {
return (
<div>
<Link to='/' style={{padding: '5px'}}>
Home
</Link>
<Link to='/component1' style={{padding: '5px'}}>
component1
</Link>
<Link to='/component2' style={{padding: '5px'}}>
component2
</Link>
<Link to='/component3'style={{padding: '5px'}}>
component3
</Link>
</div>
);
}
}
export default Header;
App.js
import React from 'react';
import './App.css';
import Routes from './routes';
function App() {
return (
<div>
<Routes />
</div>
);
}
export default App;
In your routes.js, try to change following:
From:
import { Router, Route, Switch } from 'react-router';
To:
import {
BrowserRouter as Router,
Switch,
Route,
} from "react-router-dom";

Navigate programmatically in React router 4 with mix of function/class components & TypeScript

I'm using React Router 4 in a TypeScript app where I have a React.Component that's used within a React.FunctionalComponent. I need to be able to navigate programmatically to a particular route from within the React.Component, but I can't seem to figure out how to pass the router down to the child component so that I can call this.props.history.push(). What complicates matters is that I'm using TypeScript, too.
Here's a code sandbox with a working demo of my component layout: https://codesandbox.io/s/react-programmatic-routing-xebpg
And now, the components:
app.tsx:
import * as React from 'react';
import { HashRouter } from 'react-router-dom';
import Header from './header';
import Footer from './footer';
import AppRouter from './app-router';
export default class App extends React.PureComponent {
public render() {
return (
<HashRouter>
<Header />
<AppRouter />
<Footer />
</HashRouter>
);
}
}
header.tsx:
import * as React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import { NavLink } from 'react-router-dom';
export default class Header extends React.PureComponent<any> {
public render() {
return (
<Navbar>
<Nav.Link as={NavLink} exact to="/home">
Home
</Nav.Link>{' '}
<Nav.Link as={NavLink} to="/customers">
Customers
</Nav.Link>
</Navbar>
);
}
}
app-router.tsx:
import * as React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './pages/home';
import Customers from './pages/customers';
const AppRouter: React.FC = () => {
return (
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/home" component={Home} />
<Route path="/customers" component={Customers} />
</Switch>
</div>
);
};
export default AppRouter;
pages/customers.tsx:
import * as React from 'react';
import MyFakeGrid from './customers-grid';
const Customers: React.FC = () => {
return (
<div>
<p>This is the customers page</p>
<MyFakeGrid />
</div>
);
};
export default Customers;
pages/customers-grid.tsx:
import * as React from 'react';
import { NavLink } from 'react-router-dom';
export default class MyFakeGrid extends React.Component {
public render() {
return (
<div style={{ borderColor: 'lightgray', borderStyle: 'solid' }}>
<p>
I need to be able to route programmatically from this
component
</p>
<p>
but I can't just use a NavLink like 'Home' (below), I have
to be able to navigate from within a method
</p>
<NavLink to="/home">Home</NavLink>
</div>
);
}
}
pages/home.tsx:
import * as React from 'react';
const Home: React.FC = () => {
return (
<div>
<p>This is the home page</p>
</div>
);
};
export default Home;
I've recently started learning React and I don't want to re-write my class-based components as functional components, which have become quite detailed/useful, especially not given React's gradual adoption strategy.
Base on React-router training, You can get access to the history object's properties and the closest 's match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
For example, you can re-write Customer component as blow:
import * as React from 'react';
import MyFakeGrid from './customers-grid';
import { withRouter } from "react-router";
const Customers: React.FC = () => {
return (
<div>
<p>This is the customers page</p>
<MyFakeGrid />
</div>
);
};
export default withRouter(Customers);
now you access to the history and other parameter as i said, and you can easily navigate between routes.

Routing in React JS on click

I am new to React and I want to navigate to another component on button click. I just want to perform a simple routing. This is the code that I tried. But I am not able to route it.
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom'
import Hello from './HelloComponent';
class App extends Component {
constructor(props) {
super(props);
this.try = this.try.bind(this)
}
try = () => {
alert();
<div>
<Router>
<Route path="/hello" component={Hello} />
</Router>
</div>
}
render() {
return (
<div className="App">
<div className="container">
<button id="b1" onClick={this.try}>Click me</button>
</div>
</div>
);
}
}
export default App;
Please help me with this code to perform basic routing in react JS
You cannot return JSX to onClick handler since it won't do anything with it.
You need to configure your Routes in render in advance and use history.push to change the Route
Below is a sample code that you can refer
import React, { Component } from 'react';
import { BrowserRouter as Router, Route,Switch, Redirect} from 'react-router-dom'
import Hello from './HelloComponent';
class App extends Component {
try = () => {
this.props.history.push('/hello');
}
render() {
return (
<div className="App">
<div className="container">
<button id="b1" onClick ={this.try}>Click me</button>
<Route path="/hello" component={Hello}/>
</div>
</div>
);
}
}
export default () => (
<div>
<Router>
<Route component={App} />
</Router>
</div>
);
I recommend you look at the doc.
<Route path="/hello" component={Hello}/> will display the component Hello exactly where the <Route/> is, but I think your function will do nothing here as it returns a <div> but where does it go?
You need some sort of "higher" component that will render your routes, then call a <Link/>
Then try nesting the button inside the <Link/> ?
<Link to="/??">
<button id="b1">
Click me
</button>
</Link>
in your code
try = () => {
alert();
<div>
<Router>
<Route path="/hello" component={Hello}/>
</Router>
</div>
}
your just pushing the route and it's not a action to take you to different page
bellow code will work fine and it's good practice to place router in separate component. click here you can find this code in codesandbox
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import "./styles.css";
function RouterComponet() {
return (
<Router>
<Switch>
<Route exact path="/" component={App} />
<Route path="/user" component={User} />
</Switch>
</Router>
);
}
class App extends Component {
constructor(props) {
super(props);
}
onClick = () => {
this.props.history.push("/user");
};
render() {
return (
<div>
<h1>App component</h1>
<a onClick={this.onClick} className="link">
click here
</a>{" "}
to user page
</div>
);
}
}
class User extends Component {
constructor(props) {
super(props);
}
onClick = () => {
this.props.history.push("/");
};
render() {
return (
<div>
<h1>User Componet</h1>
<a onClick={this.onClick} className="link">
click here
</a>{" "}
to back
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<RouterComponet />, rootElement);
I have created a demo that brings it all together. It has three files App.js, About.js, Contacts.js. To Navigate to any component, you need to add its route in App.js, Then depending on the location of your button (About.js), wrap it with Link that helps the element navigate to the specified route. When clicked, the Contacts component should be loaded. Hope this helps. code demo
App.js
import React from "react";
import { Switch, Route, BrowserRouter } from "react-router-dom";
import About from "./About";
import Contact from "./Contacts";
function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/" component={About} exact />
<Route path="/contacts" component={Contact} />
</Switch>
</BrowserRouter>
);
}
export default App;
About.js
import React from "react";
import { Link } from "react-router-dom";
function About() {
return (
<div>
<p>
Lorem Ipsum is simply dummy text of the printing and typesetting
industry.
</p>
<Link to="/contacts">
<button>click me</button>
</Link>
</div>
);
}
export default About;
Contacts.js
import React from "react";
function Contact() {
return <div>Call me!!</div>;
}
export default Contact;
This is the first SO post on google, so I'd like answer the question with updated coding style and answer:
From react v6 you use the useNavigation hook. (Reference)
import { useNavigate } from 'react-router-dom';
export const MyComponent = () => {
const navigate = useNavigate();
return (
<>
<button
onClick={() => {
navigate('/');
}}
>
click me
</button>
</>
);
};

How can I force a component to unmount when I navigate to a new page in React?

I have a homepage with a link to a form, like this:
import React, { Component } from 'react';
import {
BrowserRouter as Router,
Route,
NavLink,
Redirect,
Switch,
withRouter
} from "react-router-dom";
import addHand from './Forms/addHand'
export class Home extends Component {
render() {
return (
<div>
<Router>
<div>
<NavLink to= '/hands/new'> Add a new hand </NavLink>
<Route path= '/hands/new' component={addHand}/>
<h4> Search For Hands By Category </h4>
<h4> Search For Sessions By Category </h4>
<h4> Search For Tables By Category </h4>
</div>
</Router>
</div>
);
}
}
export default Home;
I also have a navbar with a link to go home from any page, like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import unmountComponentAtNode from 'react-dom';
class NavBar extends Component {
render() {
return (
<div className="navbar">
<NavLink className="link"
to="/"
exact
>Home</NavLink>
</div>
);
}
};
export default NavBar;
If I go to the form, then change my mind and decide I want to go back to the homepage, the url changes when I press the navlink, but the form is still rendered on the homepage. I can keep going back and forth between routes, but the only way to get the form to unmount from the DOM is to refresh the page. What causes this behavior, and what can I do to fix it? I have experienced similar issues before in React but have never found the solution. Thanks!
Edit** I tried adding this to the navlink:
render() {
const refCallback = node => {
unmountComponentAtNode(node)
}
return (
<div className="navbar">
<NavLink className="link"
to="/"
exact
innerRef={refCallback}
>Home</NavLink>
</div>
);
}
};
as per the react router docs, but it gives me this error:
unmountComponentAtNode(...): Target container is not a DOM element.
Here is the code in app.js
import React, { Component } from 'react';
import './App.css';
import { Router, Route } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import Home from './Components/Home'
import Navbar from './Components/Navbar';
import addHand from './Components/Forms/addHand';
export const history = createBrowserHistory();
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title-top">Hi Adam, welcome to your Personal
Poker Universe!</h1>
<h1 className="App-title-middle">Not Adam? GTFO!</h1>
<h1 className="App-title-bottom">Just Kidding, you can stay</h1>
</header>
<Router history= {history}>
<div>
<Navbar/>
<Route exact path='/' component= {Home} />
</div>
</Router>
</div>
);
}
}
export default App;
Define one Router in your App.js:
<Router history={history}>
<Switch>
<Route exact path="/" component={Home} />} />
<Route path="/hands/new" component={addHand} />
</Switch>
</Router>
then try navigating between routes it should work. Please Let me know if it doesn't work

React Router will not render Route component unless page is refreshed

It seems my application will not render the component passed to <Route /> unless I refresh the page. What could I be doing wrong?
components/App/index.jsx
// dependencies
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom'
// components
import Header from '../Header';
// containers
import SidebarContainer from '../../containers/SidebarContainer';
import MainContainer from '../../containers/MainContainer';
const App = ({store}) => (
<Provider store={store}>
<Router>
<div className="wrapper">
<Header />
<div className="container-fluid container-fluid--fullscreen">
<div className="row row--fullscreen">
<SidebarContainer />
<MainContainer />
</div>
</div>
</div>
</Router>
</Provider>
);
App.propTypes = {
store: PropTypes.object.isRequired,
};
export default App;
containers/MainContainer.jsx
// dependencies
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router-dom'
// components
import Dashboard from '../components/Dashboard';
import List from '../components/List';
// containers
import LoginContainer from './LoginContainer.jsx'
class Main extends Component {
render() {
console.log(this.props)
return(
<div className="wrapper">
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginContainer} />
<Route path="/users" component={List} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.authentication.token,
};
};
const MainContainer = connect(mapStateToProps, null)(Main);
export default MainContainer;
So it seems when I click on a <Link to="/users" /> component my path changes to http://localhost:3000/users but the component does not change from Dashboard to List
I'm also noticing that when I console.log this.props from MainContainer I do not see anything related to router such as this.props.location.pathname --perhaps I'm not structuring my application correctly?
After poking around the react-router issues page on github I found this thread: https://github.com/ReactTraining/react-router/issues/4671
It appears as though the redux connect method blocks context which is required by react-router package.
That being said, the fix for this is to wrap all redux connected components that have router components inside with withRouter() like so:
containers/MainContainer.jsx
// dependencies
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route, withRouter } from 'react-router-dom' // IMPORT withRouter
// components
import Dashboard from '../components/Dashboard';
import List from '../components/List';
// containers
import LoginContainer from './LoginContainer.jsx'
class Main extends Component {
render() {
console.log(this.props)
console.log(this.context)
return(
<div className="wrapper">
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginContainer} />
<Route path="/users" component={List} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.authentication.token,
};
};
// WRAP CONNECT METHOD
const MainContainer = withRouter(connect(mapStateToProps, null)(Main));
export default MainContainer;
I think you have to do little more tweak in your code to make it work. Assuming you use react-router v4, the following should solve your problem.
import { BrowserRouter, Route, Switch } from 'react-router-dom';
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<SidebarContainer />
<MainContainer />
</Switch>
</div>
</BrowserRouter>
</Provider>

Resources