I am trying to use nested routes to render different components. When I click my links, URL does update but the components are not rendering. Prior to this I was using imported components, but since that wasn't working, I stripped it down to this block of code and it's still just showing a blank component and no errors.
import React from 'react';
import { Route, Switch, Link, useRouteMatch } from 'react-router-dom';
function InfluencerComponent() {
let { path, url } = useRouteMatch();
const navLinks = (
<div>
<Link to={`${url}/select-trade`}>Select trade</Link>
<Link to={`${url}/add-skills`} className="ml-2">
Add skills
</Link>
</div>
);
return (
<div className="row mt-3">
<Switch>
<Route exact path={path}>
{navLinks}
</Route>
<Route path={`${path}/select-trade`}>
{navLinks}
<Test />
</Route>
<Route path={`${path}/add-skills`}>
{navLinks}
<TestTwo />
</Route>
</Switch>
</div>
);
}
function Test() {
return 'Test Component';
}
function TestTwo() {
return 'Another Test Component';
}
export default InfluencerComponent;
Components are not rendering because you should use component prop instead of children.
Example:
return (
<div className="row mt-3">
<Switch>
// ...
<Route path={`${path}/add-skills`} component={<>{navLinks}<TestTwo /></>} />
</Switch>
</div>
);
More info about <Route /> props:
https://reacttraining.com/react-router/web/api/Route/component
Related
I am new to React router here and I am trying to make clicking on a recipe in my 'BrowseRecipes' page redirect to a page dedicated to that recipe. However, when I click on the recipe, the URL shows the correct URL /browse/${recipeID}, but the page I assign to this route does not render. Only the /browse page with a list of all the recipes renders. Does anyone know why?
Here is my APP.js
import AddNewRecipe from './components/AddNewRecipe'
import BrowseRecipes from './components/BrowseRecipes'
import { currentState } from './components/redux';
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Switch, Route, Routes, Link, useParams} from "react-router-dom";
import AuthReqPage from "./components/AuthReqPage"
import Navbar from "./components/Navbar"
import RecipePage from "./components/BrowseRecipes/RecipePage"
import PageNotFound from "./components/PageNotFound"
function App(props) {
return (
<Router>
<div className="App">
<Navbar />
<Routes>
<Route path='/add' element={<AddNewRecipe />} />
<Route path='/' element={<BrowseRecipes />} />
<Route path='/browse' element={<BrowseRecipes />}>
<Route path=':recipeID' element={<RecipePage />}/>
</Route>
<Route path='/authrequired' element={<AuthReqPage />} />
<Route path='/*' element={<PageNotFound />} />
</Routes>
</div>
</Router>
);
}
export default App;
Here is my BrowseRecipe component/page:
export function BrowseRecipes (props){
console.log('browseRecipe running')
let navigate = useNavigate()
let params=useParams()
console.log(params.recipeID)
if(props.recipeStore.length>0)
{
var displayRecipes = props.recipeStore.map(
elem=>
{
return (<li key={elem.recipeID} className='recipeDisplayBox' onClick={()=>navigate(`/browse/${elem.recipeID}`)}>
{elem.title},
Prep: {elem.prepTime.numeral} {elem.prepTime.unit}
</li>)
}
)
}
return(
<div>
<h1>Browse Recipes</h1>
<h2>Your recipes:</h2>
<ul>
{displayRecipes}
</ul>
</div>
)
}
const mapStateToProps=(state)=>{
return {recipeStore: state.recipe}}
export default connect(mapStateToProps)(RequireAuth(BrowseRecipes))
And here is the individual recipe page that failed to render:
export function RecipePage (props){
console.log('RecipePage running')
let params=useParams()
return(
<div>
<h1>{params.recipeID}</h1>
</div>
)
}
const mapStateToProps=(state)=>{
return {recipeStore: state.recipe}}
export default connect(mapStateToProps)(RequireAuth(RecipePage))
"RequireAuth" here is a higher-order component that redirects the page to 'Please Sign In' page if the user is not signed in.
Did I misunderstand something about the use of UseParams? Please help me shed some light! Thank you very much
You've rendered the RecipePage component on a nested route from the "/browse" route rendering the BrowseRecipes component.
<Route path='/browse' element={<BrowseRecipes />}>
<Route path=':recipeID' element={<RecipePage />}/>
</Route>
In this configuration the BrowseRecipes is required to render an Outlet component for the nested routes to be rendered into.
Example:
import { Outlet, useNavigate, useParams } from 'react-router-dom';
export function BrowseRecipes (props) {
const navigate = useNavigate();
const params = useParams();
let displayRecipes;
if (props.recipeStore.length) {
displayRecipes = props.recipeStore.map(elem => {
return (
<li
key={elem.recipeID}
className='recipeDisplayBox'
onClick={() => navigate(`/browse/${elem.recipeID}`)}
>
{elem.title},
Prep: {elem.prepTime.numeral} {elem.prepTime.unit}
</li>
);
});
}
return (
<div>
<h1>Browse Recipes</h1>
<h2>Your recipes:</h2>
<ul>
{displayRecipes}
</ul>
<Outlet /> // <-- nested routes render here
</div>
);
}
If you don't want to render both BrowseRecipes and RecipePage at the same time, then create a nested index route specifically for BrowseRecipes.
Example:
<Route path='/browse'>
<Route index element={<BrowseRecipes />} /> // <-- "/browse"
<Route path=':recipeID' element={<RecipePage />} /> // <-- "/browse/:recipeID"
</Route>
For more information, see:
Index Routes
Layout Routes
I am beginner in React js. I am developing projects about getting pokemons with API from pokeApi. I was able to get all pokemons(name,id, and image) from there. However I want to get more detail. When I click one pokemon I want to see more detail about it.
This is my PokemonCard.js
<Router>
<StyledLink to={`pokemon/${this.state.pokemonIndex}`}>
<Card className="card">
<h5 className="card-header">{this.state.pokemonIndex}</h5>
.
.
.
</Card>
</StyledLink>
</Router>
When I click one pokemon the url changes from localhost:3000 to http://localhost:3000/pokemon/7. However, nothing shows up.
this is my Pokemon.js
import axios from 'axios';
import React, { Component } from 'react'
export default class Pokemon extends Component {
state = {
name: '',
pokemonIndex: '',
imageUrl: ''
};
async componentDidMount() {
const { pokemonIndex } = this.props.match.params;
// Urls for pokemon information
const pokemonUrl = `https://pokeapi.co/api/v2/pokemon/${pokemonIndex}/`;
const pokemonSpeciesUrl = `https://pokeapi.co/api/v2/pokemon-species/${pokemonIndex}/`;
//get information about pocekoms
const pokemonRes = await axios.get(pokemonUrl);
const name = pokemonRes.data.name;
this.setState({name})
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
</div>
)
}
}
What did I wrong? Please explain to me. I am beginner in this language. Thanks for your time and help.
Issues
You are wrapping each PokemonCard with it's own Router, you need only 1 single Router wrapping the entire app.
You've not any Route components rendering the UI.
Solution
index.js - Import a Router and wrap the app.
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById("root")
);
App.js - Render the UI components into Route components within a Switch so only a single route is rendered at-a-time.
...
import { Switch, Route } from "react-router-dom";
import Pokemon from "./components/pokemon/Pokemon";
...
function App() {
return (
<div className="App">
<NavBar />
<div className="container">
<Switch>
<Route path="/pokemon/:pokemonIndex" component={Pokemon} />
<Route path="/">
<Dashboard />
</Route>
</Switch>
</div>
</div>
);
}
PokemonCard.js - Remove the extraneous Router components.
...
render() {
return (
<div className="col-md-3 col-sm-6 mb-5">
<StyledLink to={`pokemon/${this.state.pokemonIndex}`}>
<Card className="card">
...
</Card>
</StyledLink>
</div>
);
}
I think you are missing Route with the routes you want your component to be rendered
<Switch>
<Route exact path="pokemon/:id">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I have a component included in my App.js and that component contains some common logic which needs to be executed.
So my App component JSX looks like
<CommonComponent />
{canRenderBool && (
<div class="container">
<Route exact path="/comp1">
<Comp1 />
</Route>
<Route exact path="/comp2">
<Comp2 />
</Route>
</div>
)
}
Now, I want that on each route transition (e.g. user clicks on a new route url), I want that the code in CommonComponent (non-routable component) gets triggered.
This common component has logic which returns a boolean variable and I kind of render/not render Comp1/Comp2 i.e. all the routes based on that boolean
What is the best way I can handle it. I want to avoid having that code defined/invoked in each component manually?
Also most of my components are functional/hooks based.
In case you are using functional component for your App Component. You can write your CommonComponent logic inside useEffect and then there you can set state for your canRenderBool flag
for example
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Switch, Route, Link, useLocation } from "react-router-dom";
function App() {
const [canRenderBool , setCanRenderBool] = useState(false);
const location = useLocation(); // this will only work if you wrap your App component in a Router
// this effect will run on every route change
useEffect(() => {
// your logic here
// ..
// set value of 'canRenderBool' flag
setCanRenderBool(true);
}, [location]);
return (
// Note that Router is not used here it will be wraped when we export this component, see below
<Switch>
<Route exact path="/">
<Page1 />
</Route>
{canRenderBool && (
<Route exact path="/page-2">
<Page2 />
</Route>
)}
</Switch>
);
}
function Page1() {
return (
<div>
<h1>
Go to <Link to="/page-2">Page 2</Link>
</h1>
</div>
);
}
function Page2() {
return (
<div>
<h1>
Go to <Link to="/">Page 1</Link>
</h1>
</div>
);
}
// Wrapping Router around App and exporting
export default () => (
<Router>
<App />
</Router>
);
{canRenderBool && (
<Router>
<div class="container">
<Switch>
<Route exact path="/comp1"> <Comp1 /> </Route>
<Route exact path="/comp2"> <Comp2 /> </Route>
</Switch>
<Link to="/comp1">Component 1</Link>
<Link to="/comp2">Component 2</Link>
</div>
</Router>
)
}
This might help. And you will need to import all keywords and components before using them.
I am very new to React js and trying to create a person website with a side navigation (about, education... and blogs), when I click on blogs (/blogs) it will show a list of blogs I posted and go to blog details page when I click on the blog title. My issue right now is I can see the list of blogs when I click on Blogs on the side navigation the first time, then if I click on a blog title to see a blog details, I cannot go back to blog list when click on Blogs in side navigation again. I use React Router to navigate among pages.
Below is some of my code. Thanks in advance for your help.
App.js
function App() {
return (
<Router>
<div className="App">
<Navbar />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/education" component={Education} />
<Route path="/skills" component={Skills} />
<Route path="/contact" component={Contact} />
<Route path="/blogs" component={Blogs} />
</Switch>
</div>
</Router>
);
}
Blogs.js
import React, { Component } from 'react';
import {
BrowserRouter as Router, Switch,
Route,
} from "react-router-dom";
import BlogMenu from '../components/BlogMenu';
import Blog1 from './BlogPages/Blog1';
import Blog2 from './BlogPages/Blog2';
class Blogs extends Component {
render() {
return (
<Router>
<Switch>
<Route path="/blogs" exact component={BlogMenu} />
<Route path="/blogs/1" component={Blog1} />
<Route path="/blogs/2" component={Blog2} />
</Switch>
</Router>
)
}
}
export default Blogs
BlogMenu.js
class BlogMenu extends Component {
render() {
return (
<div className="condiv">
<h1 className="subtopic">Blogs</h1>
<ul>
<BlogItem title="Blog 1" id="1" />
<BlogItem title="Blog 2" id="2" />
</ul>
</div>
)
}
}
BlogItem.js
class BlogItem extends Component {
render() {
return (
<li className="widecard" id={this.props.id}>
<Link className="blogtitle" to={`/blogs/${this.props.id}`} >{this.props.title}</Link>
</li>
)
}
}
Blog1.js
class Blog1 extends Component {
render() {
return (
<div className="condiv">
<h1 className="subtopic">Blog 1 Title</h1>
<p>Start your Blog1 contents here</p>
</div>
)
}
}
I am new to React and trying to create a layout with nested routes. Here's my scenario
show Login when URL is /
show Dashboard when URL is /dashboard
show Profile when URL is /dashboard/profile (this should load
inside the dashboard content area)
The login page and dashboard page are loading properly when the URL is accessed in the browser but for /dashboard/profile, the browser goes to a blank page instead of loading it inside the dashboard component.
Index.js
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'));
App.js
class App extends Component {
render() {
return (
<div>
{/* <Switch> */}
<Route exact path='/' component={SignIn}/>
<Route exact path='/dashboard' component={Dashboard}/>
{/* </Switch> */}
</div>
);
}
}
export default App;
Dashboard.js
class Dashboard extends React.Component {
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.root}>
<Header classes={classes} open={this.state.open} click={this.handleDrawerOpen} />
<Sidebar classes={classes} open={this.state.open} click={this.handleDrawerClose} />
<main className={classes.content}>
<div className={classes.appBarSpacer} />
*********I expect profile component to load here
but when I access the URL /dashboard/profile I get a new blank page*********
Route path="/dashboard/profile" exact component={Profile} />
</main>
</div>
</React.Fragment>
);
}
}
You need to remove the exact prop from the Dashboard route (present in Switch) while doing the child routing.
This is the minimal implementation of your use case:
import React, { Component } from "react";
import "./styles.css";
import {
NavLink,
Redirect,
Route,
BrowserRouter as Router,
Switch
} from "react-router-dom";
const App = () => (
<Router>
<div className="App">
<ul>
<li>
<NavLink to="/login">Login</NavLink>
</li>
<li>
<NavLink to="/dashboard">Dashboard</NavLink>
</li>
</ul>
<Switch>
<Route exact path="/login" component={Login} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</div>
</Router>
);
const Login = () => <span>Login Page</span>;
const Dashboard = () => {
return (
<div>
<div>Dashboard Page</div>
<NavLink to="/dashboard/profile">Go to profile</NavLink>
<div>
<Route exact path="/dashboard/profile" component={Profile} />
</div>
</div>
);
};
const Profile = () => {
return <span>Profile Page</span>;
};
export default App;
You can find the working example here:https://codesandbox.io/s/z3py3672v3