I'm trying to implement React Router v4 in my create-react-app app and my routes work except for the route with an /:id parameter as it just renders a blank page. I've searched for 2 days and tried implementing the solutions here which says to add <base href="/" /> to the head section of index.html and I've also tried following this Medium guide for simple routing but it still does not work. I don't understand, what is going on?
My code is below, any help is greatly appreciated!
Index.js
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>),
document.getElementById('root'));
registerServiceWorker();
App.js
class App extends Component {
render() {
return (
<Routes />
)
}
}
Routes.js
export const Routes = () => {
return (
<main>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/movies" component={Search} />
</Switch>
</main>
);
}
Search.js
render() {
let filteredMovies = this.state.movies.filter((movie) => {
return movie.title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
})
return (
<div>
<p>Search Page</p>
<form>
<input type="text" value={this.state.search} onChange={this.updateSearch}/>
</form>
<div>
{filteredMovies.map((movie, idx) =>
<div>
<div key={idx}>
<Link to={`/movies/${movie.videoId}`}>
<img src={movie.image.high.url} height="160px" width="100px" alt=""/>
<p>{movie.title}</p>
<p>{movie.quality}</p>
</Link>
</div>
</div>
)}
</div>
<Switch>
<Route path="/movies/:id" component={Single} />
</Switch>
</div>
)
}
}
export default Search;
Single.js
class Single extends Component {
render() {
return (
<div>
<p>Single Movie...</p>
{this.props.match.params.id}
</div>
)
}
}
export default withRouter(Single);
Every route works except for /movies/:id where it just renders a completely blank page. It doesn't even show the <p>Single Movie...</p>.
You should remove exact from route that corresponds to Search component in your Routes, i. e.:
export const Routes = () => {
return (
<main>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/movies" component={Search} />
</Switch>
</main>
);
}
The explanation is pretty simple: the exact prop means that your component will render only if current route is exactly the same as you specified in the path prop
Update
If you want to render the list of movies only when no :id is specified, you should render your list in Switch:
<Switch>
<Route exact path="/movies" render={props => {
return (
<div>
{filteredMovies.map((movie, idx) =>
<div>
<div key={idx}>
<Link to={`/movies/${movie.videoId}`}>
<img src={movie.image.high.url} height="160px" width="100px" alt=""/>
<p>{movie.title}</p>
<p>{movie.quality}</p>
</Link>
</div>
</div>
)}
</div>
)
} />
<Route path="/movies/:id" component={Single} />
</Switch>
Related
Trying to teach myself react and stuck on one part... I can't seem to get page specific content to display based on URL using useLocation() -- HELP!
App.js - router displays page on click, yay!
<Router>
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/project/projectOne" element={<Project />} />
<Route path="/project/projectTwo" element={<Project />} />
</Routes>
</Router>
Project.js - Project template serves up the components as expected
const Project = () => {
return (
<div className='content-wrapper'>
<Scroll />
<ProjectIntro />
<ProjectContent />
<ProjectGrid />
<Contact />
</div>
); }; export default Project;
ProjectIntro.js - A component trying to serve up the content -- this is where I'm stuck, useLocation() see's the path, but I can't figure out how to show the "projectIntroDetails" based on that path.
const projectOne = () => {
<h1 className='project-intro-heading'>Title Here</h1>,
<figure className='project-intro-image'>
<img src={projectImage} alt='placeholder'/>
</figure>
}
const projectTwo = () => {
<h1 className='project-intro-heading'>Title Here</h1>,
<figure className='project-intro-image'>
<img src={projectTwoImage} alt='placeholder' />
</figure>
}
const projectIntroDetails = {
projectOne: {
component: <projectOne />
},
projectTwo: {
component: <projectTwo />
}
}
const ProjectIntro = () => {
const projectPath = useLocation();
console.log(projectPath);
// this is where I need help
// how do I turn the path into seeing details to render the correct content?
const projectIntroDetail = projectIntroDetails[projectPath.pathname.split("/project/")];
return (
<div className='project-intro'>
{projectIntroDetail}
</div>
);
}; export default ProjectIntro;
You can use a component with a switch statement to determine which child component to render. This method allows you to pass any additional props to the child components.
If you don't need the <div className='project-intro'> element, you could also render the switch directly inside your ProjectIntro component.
const ProjectOne = () => {
<h1 className='project-intro-heading'>Title Here</h1>,
<figure className='project-intro-image'>
<img src={projectImage} alt='placeholder'/>
</figure>
}
const ProjectTwo = () => {
<h1 className='project-intro-heading'>Title Here</h1>,
<figure className='project-intro-image'>
<img src={projectTwoImage} alt='placeholder' />
</figure>
}
const ProjectIntros = ({ slug, ...props }) => {
switch(slug) {
case 'projectOne':
return <ProjectOne {...props} />;
case 'projectTwo':
return <ProjectTwo {...props} />;
default:
return null;
}
}
const ProjectIntro = () => {
const projectPath = useLocation();
console.log(projectPath);
return (
<div className='project-intro'>
<ProjectIntros slug={projectPath.pathname.split("/")[2]} />
</div>
);
}; export default ProjectIntro;
You don't really need to use the useLocation hook or pathname value to handle any conditional rendering logic, that's what the routing components are for.
I would suggest either passing in the correct sub-project component as a prop to be rendered on the correctly matching route, or refactoring the routes to do this in a more "react router" way.
Passing component down as prop example:
App
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/project/projectOne"
element={<Project projectIntro={<ProjectOne />} />}
/>
<Route
path="/project/projectTwo"
element={<Project projectIntro={<ProjectTwo />} />}
/>
</Routes>
</Router>
Project
const Project = ({ projectIntro }) => {
return (
<div className='content-wrapper'>
<Scroll />
<div className='project-intro'>
{projectIntro}
</div>
<ProjectContent />
<ProjectGrid />
<Contact />
</div>
);
};
Using react-router-dom to your advantage.
Project
Convert Project into a layout component and render the ProjectOne and ProjectTwo components on nested routes. Layout routes are intended to be used to share common UI elements and layout, and render routed content into an outlet.
import { Outlet } from 'react-router-dom';
const Project = () => {
return (
<div className='content-wrapper'>
<Scroll />
<div className='project-intro'>
<Outlet /> // <-- render nested routes here
</div>
<ProjectContent />
<ProjectGrid />
<Contact />
</div>
);
};
App
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/project" element={<Project />}>
<Route path="projectOne" element={<ProjectOne />} />
<Route path="projectTwo" element={<ProjectTwo />} />
</Route>
</Routes>
</Router>
I want to add routing to my app but the "Link" I made in a child component doesn't work onClick, but only when I refresh the page. I guess the problem is the way too much nesting but I have no idea how can I solve it.
One mention: I imported BrowserRouter as Router everywhere.
This is the file structure
This is the code spippets that related to my problem:
App component:
function App() {
return (
<Router >
<div className="App">
<Switch>
<Route exact path="/" component={NewFetch} />
<Route path="/cardID/:id" component={Details} /> //The route that doesn't work
</Switch>
</div>
</Router>
NewFetch (Main) component:
<Router> //Tried with <React.Fragment>
...
<Route path={["/cards/:name", "/cards/:filter"]}>
<Filter isLoaded={isLoaded} handleScroll={handleScroll} toScrollTop={toScrollTop} value={value}
scrollPosition={scrollPosition} jumpToTop={jumpToTop} testFilter={testFilter} />
</Route>
</Router>
Card (child 2) component from :
const Card = (props) => {
return (
<div className={props.img ? "card" : "hide"}>
<Link to={`/cardID/id=${props.id}`} > //Link that doesn't connect
<img src={props.img} alt={props.name} />
</Link>
</div>
)
};
So basically I can't connect the "Link" from a hardly nested component.
function App() {
return (
<Router >
<div className="App">
<Switch>
<Route exact path="/" component={NewFetch} />
<Route path="/cardID/:id" component={Details} /> //The route that doesn't work
</Switch>
</div>
</Router
const Card = (props) => {
return (
<div className={props.img ? "card" : "hide"}>
<Link to={`/cardID/id=${props.id}`} > //Link that doesn't connect
<img src={props.img} alt={props.name} />
</Link>
</div>
)
};
Above is your code which might look right but the is a slight bug here:
The bug is in the wrong way you are linking to path="/cardID/:id
What you are to do is in your Card child2 is:
const Card = (props) => {
return (
<div className={props.img ? "card" : "hide"}>
<Link to={`/cardID/${props.id}`} > //Removed **id=....**
<img src={props.img} alt={props.name} />
</Link>
</div>
)
};
This is what you have to understand that when you make a route like so path="/route/:id" the :id is just a placeholder waiting for you to place anything so id is commonly used so your code makes sense and mainly basically you want to route based on id but one could have written :cat for example but that is just a placeholder
I am using react-router for programatic navigation in my app. All is fine expect for this new feature I want to add, that requires conditional routing.
I need to render, for instance profileRoutes when location is /users/profile and somethingElse when location is /users/details for instance. The code below works fine for now as I only have profileRoutes built. But I consolidated the routes into one, and need /users/* or something to that effect, such that that location would match all routes related to /users/...
<Route
children={({ location }) => (
<div>
{
location.pathname === "/users/profiles" ?
<profileRoutes></profileRoutes> :
<otherComponent/>
}
</div>
)}
/>
My question: Is there a react-router way to achieve this? like location.pathname.startswith.... sort of thing?
For your requirements, please use the parameter exact.
To match either /users or /users/profiles or /users/what-ever:
<Route path="/users" component={Users} />
To match only /users
<Route exact path="/users" component={Users} />
In principle, let do as follows:
In the main component, use Route without exact to point to
the component Users
In component Users, use Route again to match
sub-components of Users
Here is the example:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
export function RouterExample() {
return (
<Router>
<div>
<ul>
<li>Main Menu:</li>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/users" component={Users} />
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Welcome home</h2>
</div>
);
}
function Users({ match }) {
return (
<div>
<h2>Welcome to Users</h2>
<ul>
<li>Users menu:</li>
<li>
<Link to={`${match.url}/profiles`}>User Profiles</Link>
</li>
<li>
<Link to={`${match.url}/settings`}>User Settings</Link>
</li>
</ul>
<Route exact path={`${match.path}`} component={UserHome} />
<Route path={`${match.path}/profiles`} component={UserProfiles} />
<Route path={`${match.path}/settings`} component={UserSettings} />
<Route path={`${match.path}/:section`} component={UserFooter} />
</div>
);
}
function UserHome() {
return (
<div>
<h3>UserHome Component</h3>
</div>
);
}
function UserProfiles() {
return (
<div>
<h3>UserProfiles Component</h3>
</div>
);
}
function UserSettings() {
return (
<div>
<h3>UserSettings Component</h3>
</div>
);
}
function UserFooter({ match }) {
return (
<div>
<hr />
UserFooter Component is always visible under /users/*
<br />
You are now at
<br />
path: {match.path}
<br />
match params: {match.params.section}
</div>
);
}
Live demo at codesandbox
Thanks for pointing me in the correct direction, ie using match!
I didn't need to modify my routes though (I like to keep my components separate and just import instead. I do have an exact tag on the root component, /.
Here's what worked for me:
<Route
children={({ match }) => (
<div>
{
match.path == "/users" ?
<profileRoutes></profileRoutes> :
<otherComponent/>
}
</div>
)}
/>
Now when match.path == "/users" is true, all routes downstream ie /users/etc are rendered.
Hope this helps someone else!
The component tree i want is as below
- Login
- Home
- Contact
- About
Contact and About are children of Home.
This is my App.js ,
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Route exact path="/home" component={HomeView} />
</div>
</BrowserRouter>
);
}
}
render(<App />, document.getElementById('root'));
This is Home,
export const HomeView = ({match}) => {
return(
<div>
<NavBar />
Here i want to render the contact component, (Navbar need to stay)
</div>
)
}
This is my Navbar,
export const NavBar = () => {
return (
<div>
<Link to="/home">Home</Link>
<Link to="/home/contact">Contact</Link>
<hr/>
</div>
)
}
Contact component just need to render "hello text".
To make nested routes you need to remove exact:
<Route path="/home" component={HomeRouter} />
And add some routes:
export const HomeRouter = ({match}) => {
return(
<div>
<NavBar />
{/* match.path should be equal to '/home' */}
<Switch>
<Route exact path={match.path} component={HomeView} />
<Route exact path={match.path + '/contact'} component={HomeContact} />
<Switch>
</div>
)
}
You don't need use match.path in nested routes but this way it will be easier to move everything from "/home" to "/new/home" in case you decide to change your routing.
I'm trying to use routing in two different components. First is the SIDEBAR component where links are there.... and a dynamic SHOWPANE component where other components will be rendered based on the routes provided by the sidebar.
Here is what I've tried...
//The App component
class App extends Component {
render() {
return (
<div className="App">
<div className="app-con">
<Sidebar />
<Showpane />
</div>
</div>
);
}
}
//The sidebar component
const sidebar = props => (
<>
<div className="sb-con">
<BrowserRouter>
<div>
<Link to="/" className="navs">
My Account
</Link>
<Link to="/history" className="navs">
Account History
</Link>
<Link to="/settings" className="navs">
Account Settings
</Link>
<Link to="/" className="navs">
Log out
</Link>
</div>
</BrowserRouter>
</div>
</>
);
//The showpane component
const showpane = props => (
<>
<div className="sp-con">
<BrowserRouter>
<div>
<Route path="/" component={Account} exact />
<Route path="/history" component={Acchistory} />
<Route path="/settings" component={Accset} />
</div>
</BrowserRouter>
</div>
</>
);
I expect that after clicking Links in the sidebar component the showpane component must be rendered.
The BrowserRouter is your applications router, and therefore should sit at the top of your application's heirarchy.
class App extends Component {
render() {
return (
<Router>
<div className="App">
<div className="app-con">
<Sidebar />
<Showpane />
</div>
</div>
</Router>
);
}
}
Your showpane will then look like:
const showpane = props => (
<>
<div className="sp-con">
<div>
<Route path="/" component={Account} exact />
<Route path="/history" component={Acchistory} />
<Route path="/settings" component={Accset} />
</div>
</div>
</>
);
I would also recommend looking at the sidebar example reacttraining's ReactRouter Docs since that is what you seem to be going for.