React router exact matching only updating URL without re-render of DOM - reactjs

I am connected to an API (The Movie DB) which is returning a list of results (Movies), and when I click into one of the results of the API I need to see a detail page. I have set up dynamic routing, however when I click for the detail page, my URL changes to something like: 'http://localhost:3000/moviedetails/2503', where the 2503 is a unique item ID. However, the DOM is not re-rendering. When I refresh with the same URL, I am then forwarded to the correct page - but I need this to happen upon the click of the link to the details page.
All questions I have seen online have stated that you must use the exact keyword within your routes to fix this, however I am using the exact keyword on all routes except the dynamic one, and still encountering the issue of the DOM not re-rendering.
My App.js file is as follows:
import React from 'react';
import './App.css';
import Home from './components/Home';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import About from "./components/About.js"
import Discover from './components/Discover';
import Favourites from './components/Favourites';
import Rated from './components/Rated';
import Nav from './components/Nav';
import MovieDetails from './components/MovieDetails'
function App() {
return (
<Router>
<div className="App">
<Nav />
<Switch>
<Route path="/moviedetails/:id" component={MovieDetails}/>
<Route path="/" exact component={Home} />
<Route path="/about" exact component={About} />
<Route path="/discover" exact component={Discover} />
<Route path="/favourites" exact component={Favourites} />
<Route path="/rated" exact component={Rated} />
</Switch>
</div>
</Router>
);
}
export default App;
My MovieRow.js (which includes the link to the details page) is as follows:
import React from "react";
import { Link } from "react-router-dom";
class MovieRow extends React.Component {
render() {
return (
<div>
<table key={this.props.movie.id}>
<tbody>
<tr>
<td>
<img width="150" alt="poster" src={this.props.movie.poster_src} />
</td>
<td>
<h3>{this.props.movie.title}</h3>
<p>{this.props.movie.overview}</p>
<Link to={`/moviedetails/${this.props.movie.id}`}>
View Movie Details
</Link>
</td>
</tr>
</tbody>
</table>
</div>
);
}
}
export default MovieRow;
My MovieDetails component (which is to be returned when link to details is clicked on) is not yet connected to the API and only contains the following:
import React from "react";
import { Link } from "react-router-dom";
class MovieDetails extends React.Component {
render() {
return (
<div>
movie details
</div>
);
}
}
export default MovieDetails;

Hi William welcome to the StackOverflow community:
Can you please check your console if you are getting any kind of error or something:
as this POC: https://codesandbox.io/s/blissful-dust-nrcrj works fine with the following components.
index.js
---------
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Rated from "../components/Rated";
import Home from "./Home";
function App() {
return (
<Router>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/rated" exact component={Rated} />
</Switch>
</Router>
);
}
export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Home.js
-------
import React from "react";
import { withRouter } from "react-router";
const Home = props => {
return (
<button onClick={() => props.history.push("/rated")}>GOTO RATED</button>
);
};
export default withRouter(Home);
Rated.jsx
---------
import React from "react";
const Rated = () => <h1>Rated</h1>;
export default Rated;

Related

React Component doesn't load even if I write exact path in the browser as defined in the routes

I'm a beginner in React and stuck with some problems. As I have defined my routes into 3 specific Components. Please find them below.
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
import {Provider} from 'react-redux';
import store from './Redux/store';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter >
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
App.js
//import logo from './logo.svg';
import './App.css';
import SignupAndSignInComponent from './Components/Sign-In and Sign-Up Component/Sign-in.and.signup.component';
import {Route,Switch} from 'react-router';
//import Menu from './Components/Menu-Component/menu.component';
//import sideNavBar from './Components/SideNavBar Component/Sidenavbar.component';
import Admin from './Components/Admin-Component/admin.component';
function App() {
return (
<Switch>
<Route exact path="/" component={SignupAndSignInComponent}/>
<Route path="/admin" component={Admin} />
</Switch>
);
}
export default App;
admin.component.jsx
import React from 'react';
import { Route } from 'react-router-dom';
//import Employees from '../Employees/employees.component';
import {Switch} from 'react-router-dom';
import Menu from '../Menu-Component/menu.component';
//import EmployeeEdit from '../EmployeeEdit/employeeEdit.component';
import LeaveTypeEdit from '../LeaveTypeEdit/leaveTypeEdit.component';
import LeaveTypes from '../LeaveTypes/leavetypes.component';
import Department from '../Department/department.component';
import DepartmentEdit from '../DepartmentEdit/departmentEdit.component';
import EmployeeRouteComponent from '../EmployeeRoute-Component/employeeroute.component';
//<Route path={`${match.path}/employees/edit/:email`} component={EmployeeEdit} />
//<Route path={`${match.path}/employees/edit`} component={EmployeeEdit} />
const Admin = ({ match }) => (
<div className='shop-page'>
<Menu/>
<Switch>
<Route exact path={`${match.path}/employees`} component={EmployeeRouteComponent} />
<Route exact path={`${match.path}/leavetypes`} component={LeaveTypes}/>
<Route path={`${match.path}/leavetypes/edit/:leave_id`} component={LeaveTypeEdit}/>
<Route exact path={`${match.path}/department`} component={Department} />
<Route path={`${match.path}/department/edit/:department_id`} component={DepartmentEdit} />
<Route path={`${match.path}/department/edit`} component={DepartmentEdit} />
</Switch>
</div>
);
export default Admin;
employeeroute.component.jsx
import React from 'react';
import Employees from '../Employees/employees.component';
import EmployeeEdit from '../EmployeeEdit/employeeEdit.component';
import { Route } from 'react-router-dom';
import {Switch} from 'react-router-dom';
import {withRouter} from 'react-router-dom';
const EmployeeRouteComponent=({ match })=>
{
console.log("kjsdj",match.path);
return(
<Switch>
<Route exact path={`${match.path}/data`} component={Employees} />
<Route path={`${match.path}/data/edit/:email`} component={EmployeeEdit} />
<Route path={`${match.path}/data/edit`} component={EmployeeEdit} />
</Switch>
);
}
export default withRouter(EmployeeRouteComponent);
employees.component.jsx
import axios from 'axios';
import React, { useEffect,useState } from 'react';
import './employees.styles.scss';
import EmployeeDetails from '../EmployeeDetails/employeedetails.component';
import CustomButton from '../Custom-Button-Component/custom-button.component';
//import {withRouter} from 'react-router-dom';
import {connect} from 'react-redux';
import setEmployees from '../../Redux/Action/employees.action';
const Employees=(props)=>
{
//const[employeeData,setEmployeeData]=useState(null);
useEffect(
async()=>{
const response=await axios.get("http://localhost:8080/api/v1/employeedetails");
const data=response.data;
console.log(data);
props.setEmployees(data);
},
[JSON.stringify(props.employees)] );
//cellpadding="0" cellspacing="0"
console.log(props.employees);
return (
<section className="table-responsive">
<h3 style={{color: "red"}}>{props.message}</h3>
<h1>Employee Details</h1>
<CustomButton onClick={()=>props.history.push(props.match.path+"/edit/-1")}>Add Employee</CustomButton>
<div className="tbl-header">
<table border="0">
<thead>
<tr>
<th>EmailId</th>
<th>UserType</th>
<th>Department</th>
<th>Reporting</th>
<th>Designation</th>
<th>PhoneNumber</th>
<th>Action</th>
</tr>
</thead>
</table>
</div>
<div className="tbl-content">
<table border="0">
<tbody>
{props.employees?props.employees.map(employee=><EmployeeDetails key={props.employees.email_id}
email_id={employee.email_id}
department={employee.department}
designation={employee.designation}
phone_number={employee.phone_number}
reporting={employee.reporting}
user_type={employee.user_type}
/>):<EmployeeDetails/>}
</tbody>
</table>
</div>
</section>
);
}
const mapStateToProps=(state)=>({
message:state.login.message,
employees:state.login.employees
});
const mapDispatchToProps=(dispatch)=>({
setEmployees:employees=>dispatch(setEmployees(employees))
})
export default connect(mapStateToProps,mapDispatchToProps)(Employees);
And as you can see I have defined Employee Component route as "http://localhost:3000/admin/employees/data" but still, it doesn't load the component and can't even log anything when I routes to this specific route.
But everything is working fine when I'm accessing all the routes defined in the "Admin Component" but not able to access the routes defined in the "EmployeeRoute" component.
Please let me know if anything is missing from my side.
The problem is in the way that you have your various Switch blocks rendered relative to one another. Specifically it's due to using exact incorrectly.
Let's think about how we expect the /admin/employees/data path to find it's component.
You have this statement here:
<Route exact path={`${match.path}/data`} component={Employees} />
inside of an EmployeeRouteComponent.
That EmployeeRouteComponent is loaded from inside Admin here:
<Route exact path={`${match.path}/employees`} component={EmployeeRouteComponent} />
But you are only loading EmployeeRouteComponent if the url is an exact match to "/admin/employees/". Your URL "/admin/employees/data" is not an exact match so it skips over this condition. It looks through all of the possibilities in Admin and finds no match, so it renders a div with a Menu and no contents.
You need to remove the exact from your Route and I would remove all of your other exacts while you're at it. Instead put more specific URLs before shorter ones (as the router will select the first match). For example, if you move your Admin route before your SignupAndSignInComponent, any URL which does not start with "/admin" will load the sign-up.

React-router-dom: Clicking on Link adds the route to the Url Link indefinitely

After several attempts, I have managed to implement basic nested-routing with React-router-dom.
Here's the simple project structure:
Here are the relevant files:
App.js
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import ParentComponent from "./Components/nestedComponents/ParentComponent";
import NavBar from "./Components/Shared/NavBar";
function App() {
return (
<div className="App">
<BrowserRouter>
<NavBar />
<Switch>
<Route path="/home" name="Home" component={ParentComponent} />
</Switch>
</BrowserRouter>
</div>
);
}
export default App;
NavBar.js
import React from "react";
import { Link } from "react-router-dom";
export default function NavBar() {
return (
<div>
<Link to={`home/nestedComponentOne`}> Nested Component One </Link>
<Link to={`home/nestedComponentTwo`}> Nested Component Two </Link>
</div>
);
}
ParentComponent.js
import React from "react";
import nestedComponentOne from "./nestedComponentOne";
import nestedComponentTwo from "./nestedComponentTwo";
import { Switch, Route } from "react-router-dom";
export default function ParentComponent() {
return (
<div>
<Switch>
<Route path="/home/nestedComponentOne" component={nestedComponentOne} />
<Route path="/home/nestedComponentTwo" component={nestedComponentTwo} />
</Switch>
</div>
);
}
nestedComponentOne.js
import React from "react";
export default function nestedComponentOne() {
return <div>NESTED COMPONENT 1</div>;
}
nestedComponentTwo.js
import React from "react";
export default function nestedComponentTwo() {
return <div>NESTED COMPONENT 2</div>;
}
So here's the Result:
If I click on nestedComponentOne:
If I click on nestedComponentTwo:
The problem is when I click again on nestedComponentOne (or Two) after the I have clicked it the first time, the route gets added to the url string instead of replacing it:
Some update need for your code.
Working Demo
NavBar.js
Here you forget to add slash / at front to link from root.
<Link to={`/home/nestedComponentOne`}> Nested Component One </Link>
<Link to={`/home/nestedComponentTwo`}> Nested Component Two </Link>
ParentComponent.js
As we removed the Switch from this component, so we need to get the matching information from parent router and pass the path to navigate the corresponding your nested component
export default function ParentComponent({ match }) {
return (
<div>
<Route path={`${match.path}/nestedComponentOne`} component={nestedComponentOne} />
<Route path={`${match.path}/nestedComponentTwo`} component={nestedComponentTwo} />
</div>
);
}
Why don't you try putting all the route in one file. Something like this:
<Route exact path="/home" name="Home" component={ParentComponent} />
<Route path="/home/nestedComponentOne" component={nestedComponentOne} />
<Route path="/home/nestedComponentTwo" component={nestedComponentTwo} />

React not rendering Route Components

I am building a consumer facing app with a admin dashboard. I want to keep the routing separate for them and so trying to delegate :-
App.js
import React, {Component} from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
//styles
import './style/bootstrap/bootstrap.scss';
//apps
import Mainapp from './mainapp/Mainapp';
import Admin from './admin/Admin';
const MainappContainer = () => (
<Mainapp />
);
const AdminContainer = () => (
<Admin />
);
class App extends Component{
render(){
return (
<Router>
<Switch>
<Route path="/admin" component={AdminContainer}/>
<Route path="/" component={MainappContainer}/>
</Switch>
</Router>
)
}
}
export default App;
Admin.js
import React, {Component} from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
//styles
import './admin-style.scss';
//layout
import ControlPanel from './component/layout/ControlPanel';
import Navbar from './component/layout/Navbar';
//pages
import Quote from './component/pages/quote/Quote';
class Admin extends Component{
render(){
return (
<div className="adminWrapper">
<ControlPanel />
<section className="viewPanel">
<Navbar />
<Router>
<Route path="/quote" component={Quote}/>
</Router>
</section>
</div>
)
}
}
export default Admin;
However when I hit the URL
http://localhost:3000/admin/quote
it doesn't seem to load the quote component
Quote.js
import React, { Component } from 'react';
class Quote extends Component {
render() {
return (
<div className="float-right pr-3">
<h3>
Quote Page
</h3>
</div>
)
}
}
export default Quote;
When dealing with nested subroutes, the easiest solution is to use match.
path - (string) The path pattern used to match. Useful for building nested
Routes.
url - (string) The matched portion of the URL. Useful for building
nested Links.
By design, components placed inside a Route's component render method are given several additional props from react-router-dom. Among them are history and match. You can leverage these props to either to match against sub routes and/or to control browser history location.
In addition, you only need one instance of BrowserRouter sitting at the top-level of the application, then you can use Switch to optionally render any main or sub routes. And you don't need to use class components unless you're utilizing state and/or a class field.
A very basic, rudimentary working example of your application:
src/components/Admin/index.js
import React from "react";
import { Switch, Link, Route } from "react-router-dom";
import ControlPanel from "../ControlPanel";
import Quote from "../Quote";
// since Admin is placed inside a Route's component render
// method, it has access to history and match
function Admin({ history, match }) {
return (
<div className="adminWrapper">
<ControlPanel />
<section className="viewPanel">
<Link to={`${match.url}/quote`}>View quote</Link>
<br />
<Switch>
<Route exact path={`${match.path}/quote`} component={Quote} />
</Switch>
</section>
<br />
<button type="button" onClick={() => history.goBack()}>
Go Back
</button>
</div>
);
}
export default Admin;
index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
import Admin from "./components/Admin";
const linkStyle = {
padding: "0 10px"
};
function App() {
return (
<BrowserRouter>
<Link style={linkStyle} to="/">
Home
</Link>
<Link style={linkStyle} to="/admin">
Admin
</Link>
<Switch>
<Route path="/admin" component={Admin} />
<Route path="/" render={() => <h1>Main App</h1>} />
</Switch>
</BrowserRouter>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
Follow the Nested Routing Example
The main changes you need to do are:
1. Remove the <Router></Router> from Admin component and
2. Prepend match.path to "/quotes", like it is done in Topics component in the example. In the example, Topics is a function component so it is receiving match as function parameter. As your Admin component is class component, you can access it as this.props.match in render method.
<Route path={`${this.props.match.path}/quote`} component={Quote}/>
<Route exact path="/admin" component={AdminContainer}/>
<Route exact path="/admin/quote" component={Quote}/>
This won't route you to /admin/quote instead it will route you to /admin/admin/quote.
Since it is inside admin just /quote is enough
<Route path="/admin" component={AdminContainer}/>
<Route path="/quote" component={Quote}/>

React - inserting variable into Route path

I have an app that I am creating and am wondering how you would insert variables into the <Route path={insert variable here} component={myProfile}> I am trying to create a myProfile page and I am trying to get it so when they click onto the link, it redirects them to http://mywebsite.com/userId but when I try to create a Route with a variable in the path argument, it does not return the component I am trying to render when on that path.
routes.js
import { Meteor } from "meteor/meteor"
import React from "react";
import { withRouter, Switch, BrowserRouter, Route, Redirect, Link } from "react-router-dom";
import Login from "../ui/authentication/Login";
import Signup from "../ui/authentication/Signup";
import Home from "../ui/Home";
import { SubjectRoutes } from "../ui/subjectRoutes/subjectRoutes";
import AddNote from "../ui/AddNote";
import myProfile from "../ui/myProfile";
import NotFound from "../ui/NotFound";
export default class Routes extends React.Component{
renderSubjectRoutes(subjects){
return subjects.map((subject) => {
return <Route key={subject.name} path={subject.path} component={subject.component}/>
})
}
render(){
return (
<div>
<BrowserRouter>
<Switch>
<Login path="/login" />
<Signup path="/signup" />
<Route path="/" component={Home} exact/>
{this.renderSubjectRoutes(SubjectRoutes)}
<AddNote path="/addNote"/>
<myProfile path={Meteor.userId()} /> //<-- Here
<NotFound />
</Switch>
</BrowserRouter>
</div>
)
}
}
Menu.js
import { Meteor } from "meteor/meteor"
import React from "react";
import { withRouter, Link } from "react-router-dom";
import { SubjectRoutes } from "./subjectRoutes/subjectRoutes";
import AddNote from "./AddNote";
class Menu extends React.Component{
renderMenu(items){
return items.map((item) => {
return <p key={item.name}><Link to={item.path}>{item.name}</Link></p>
})
}
render(){
return(
<div>
<h1>Menu</h1>
{this.renderMenu(SubjectRoutes)}
<p><Link to="/addNote">Add a Note</Link></p>
<p><Link to={Meteor.userId()}>My Profile</Link></p>
</div>
)
}
}
export default withRouter(Menu)
You are creating way more work for yourself, and this is the wrong way to add variables to route. What you're looking to do is add params to your route. In your case, you would want it to look something like this.
<Route path="/user/:userId" />
The : is what denotes that it is a parameter, ready to render a path based on the userId. So if you went to route /user/123 - it would be able to render user 123's data.
Here's some documentation to help you out.
https://reacttraining.com/react-router/web/example/url-params

React Router: Render new view without page refresh

Hello! What I'm trying to do is rework my react-router so the NavLink renders a completely new page on click, instead of rendering at the bottom of the div, as shown in the gif above.
Here's the content of my main App.js component:
import React, { Component } from 'react';
import './App.css';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Home from './Home.js';
import About from './About.js';
import September from './September.js';
import Trilogy from './Trilogy.js';
class App extends Component {
render() {
return (
<Router>
<div>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about/' component={About} />
<Route path='/september/' component={September} />
<Route exact path='/september/trilogy/' component={Trilogy} />
</Switch>
</div>
</Router>
);
}
}
export default App;
The Home component's code, which holds the NavBar that's used in the Home Page.
import React, { Component } from 'react';
import { BrowserRouter as Router, NavLink, Switch, Route } from 'react-router-dom';
import logo from './logo.png';
import About from './About.js';
import September from './September.js';
import Trilogy from './Trilogy.js';
let NavBar = () => {
return (
<div>
<h2 className="container2"><NavLink to='/about/'>About</NavLink> </h2>
<img src={logo} className="somersetLogo" alt="somersetLogo" />
<h2 className="container" >Contact</h2>
</div>
)
}
class Home extends Component {
render() {
return (
<Router>
<div>
<NavBar />
<Switch>
<Route exact path='/about/' component={About} />
</Switch>
</div>
</Router>
)
}
}
export default Home;
Any idea what went wrong? Any help would be appreciated! Thanks!
If you are using react router v4 or above it should be something like this.
import { Link } from 'react-router-dom';
<Link to='/about'>
About
</Link>
Why you are defining router again in Home component which is not needed. Keeping route configuration in App component would be enough. Hope this helps. Happy coding !

Resources