useParams() for none route components in ReactJS - reactjs

Please go through my App.js
<Provider store={reduxStore}>
<Router basename={process.env.PUBLIC_URL}>
<Container>
<Navigation /> <----------------------------------- HERE
<div className="wrapper">
<Switch>
<PublicRoute exact path={PATHS.HOME} component={Home} />
</Switch>
</div>
<Footer/> <---------------------------------------- HERE
</Container>
</Router>
</Provider>
Navigation is use in all components so I added that component in App.js. For sure, this component has no route linked to it. BUT:
In Navigation and Footer, I want to use useParams().
Navigation.js
import React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import { withRouter, useParams } from 'react-router-dom';
const Navigation = (props) => {
const { slug } = props;
return (
<>
<Navbar>
<Nav>
<Nav.Link>
Home
</Nav.Link>
</Nav>
</Navbar>
</>
);
};
export default withRouter(Navigation);
My current url:
localhost:3000/event/:slug
I do recieve props, but I could not find slug

try remove exact ?
<Switch>
<PublicRoute path={PATHS.HOME} component={Home} />
</Switch>

Related

React Router need help routing

I'm a beginner front-end dev and I try to make an imaginary website. I'm learning to react router now. So when I try to click on the home/about or contact button, It doesn't open it. What am I doing wrong?
here's my code below :
my App code :
import React from 'react';
import './App.css';
import Navibar from './Components/Navibar';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Home from './Components/Home';
import About from './Components/About';
import Contact from './Components/Contact';
let App = () => {
return (
<Router>
<div className="App">
<Navibar/>
<div className="content">
<Switch>
<Route exact path="/">
<Home/>
</Route>
<Route exact path="/About">
<About/>
</Route>
<Route exact path="Contact">
<Contact/>
</Route>
</Switch>
</div>
</div>
</Router>
);
}
export default App;
and here's my navbar code :
import React from 'react';
import {Nav, Navbar, Container} from 'react-bootstrap';
import {Link} from 'react-router-dom';
let Navibar = () => {
return (
<>
<Navbar bg="dark" variant="dark">
<Container>
<Navbar.Brand href="#home">My Portfolio</Navbar.Brand>
<Nav className="me-auto">
<Link to="/">Home</Link>
<Link to="#about">About me</Link>
<Link to="#contact">Contact</Link>
</Nav>
</Container>
</Navbar>
</>
);
}
export default Navibar;
and here's my Home (the home/about and contact codes are the same) code :
import React from 'react';
let Home = () => {
return (
<>
<h2>Home</h2>
</>
);
}
export default Home;
I need help from a professional, because I'm getting stuck in this :)
The Navibar should link to the route paths you are rendering, not anchor tags (hashes) on the current page. The bootstrap Navbar.Brand component should also render a RRD Link component instead of a plain anchor tag with href attribute.
import React from 'react';
import { Nav, Navbar, Container} from 'react-bootstrap';
import { Link } from 'react-router-dom';
const Navibar = () => {
return (
<>
<Navbar bg="dark" variant="dark">
<Container>
<Navbar.Brand
as={Link} // <-- as a RRD Link component
to="/" // <-- to prop for Link component
>
My Portfolio
</Navbar.Brand>
<Nav className="me-auto">
<Link to="/">Home</Link> // <-- "/"
<Link to="/about">About me</Link> // <-- "/about"
<Link to="/contact">Contact</Link> // <-- "/contact"
</Nav>
</Container>
</Navbar>
</>
);
}
export default Navibar;
The App component should specify route paths that match what the navbar is linking to.
Also the routes App is rendering within the Switch component should be rendered in inverse order of route path specificity. In other words, render the more specific paths prior to the less specific paths. This is to attempt to match and render more specific paths and falling back to less specific paths. This also effectively eliminates the need to use the exact prop.
const App = () => {
return (
<Router>
<div className="App">
<Navibar />
<div className="content">
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</div>
</Router>
);
}

My react onClick navigate shows URL, but does not render component

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

createBrowserHistory is not working. The redirects are updating the URL, but not rendering the component

I'm using React Router v5 with a custom history object and redirects aren't working properly. I'm using react-router-dom 5.x ("^5.2.0") and history 4.x ("4.10.1"). My redirects are updating the URL, but not rendering the component without reloading the page.
AppRouter.tsx
import React from "react";
import {Route, Router, Switch} from "react-router-dom";
import {createBrowserHistory} from "history";
import {paths} from "../configs/routerConfigs";
import Main from "../modules/Main";
import Blog from "../modules/Blog"
import Store from "../modules/Store"
import Gallery from "../modules/Gallery";
const history = createBrowserHistory();
const AppRouter = () => {
return (
<Router history={history}>
<Switch>
<Route
path={paths.blog()}
exact
>
<Blog/>
</Route>
<Route
path={paths.store()}
exact
>
<Store/>
</Route>
<Route
path={paths.gallery()}
exact
>
<Gallery/>
</Route>
<Route path={paths.main()} exact>
<Main />
</Route>
</Switch>
</Router>
);
};
export default AppRouter;
this is the header from where I redirect to another link
index.tsx
const AppHeader: React.FC = () => {
return (
<BrowserRouter>
<Header className="app-header">
<Row gutter={16}>
<Col span={6}>
<div className="logo">
<img src={Logo} alt={"cosmonet logo"}/>
<span>Cosmonet</span>
</div>
</Col>
<Col span={12}>
<nav className={"nav"}>
<Menu className="nav-list" mode="horizontal" >
<Menu.Item key="1" className="nav-list-items"><Link to={paths.main()}>{"Main"}</Link></Menu.Item>
<Menu.Item key="2" className="nav-list-items"><Link to={paths.blog()}>{"Blog"}</Link></Menu.Item>
<Menu.Item key="3" className="nav-list-items"><Link to={paths.store()}>{"Store"}</Link></Menu.Item>
<Menu.Item key="4" className="nav-list-items"><Link to={paths.gallery()}>{"Gallery"}</Link></Menu.Item>
</Menu>
</nav>
</Col>
<Col span={6}>
<Button text={"log out"}/>
</Col>
</Row>
</Header>
</BrowserRouter>
);
};
export default AppHeader;
I tried another versions of react-router-dom and history but none of them seems to work properly. I even tried to use forceRefresh: true but it doesn't work either :/
I don't know where the AppHeader is rendered in relation to the AppRouter, and vice-versa, but you are using two routers when you need only one routing context for your entire app.
The issue is that any navigation actions by the links in the AppHeader will be handled by the BrowserRouter you've used there. The Router wrapping the routes isn't made aware of any changes, that is until you refresh the page and it remounts and reads the URL path.
I suggest removing the Router & history from AppRouter and the BrowserRouter from AppHeader. Move the Router and custom history object higher up in the ReactTree to allow providing a routing context to the entire app.
Example:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from "react-router-dom";
import { createBrowserHistory } from "history";
import App from "./App";
const history = createBrowserHistory();
ReactDOM.render(
<StrictMode>
<Router history={history}>
<App />
</Router>
</StrictMode>,
document.getElementById("root")
);
AppRouter
const AppRouter = () => {
return (
<Switch>
<Route path={paths.blog()} exact>
<Blog/>
</Route>
<Route path={paths.store()} exact>
<Store/>
</Route>
<Route path={paths.gallery()} exact>
<Gallery/>
</Route>
<Route path={paths.main()} exact>
<Main />
</Route>
</Switch>
);
};
AppHeader
const AppHeader: React.FC = () => {
return (
<Header className="app-header">
...
</Header>
);
};

How to show/hide reactstrap navbar in Reactjs Redux application?

I want to show/hide the navigation bar depending upon logged in status of the user. Once the user is authenticated, I store the access-token in local storage. I have tried to show/hide the navigation bar by checking if access-token exists or not. But it needs a hard refresh.
Please find the header component: /components/app-header.js
const AppHeader = () => (
<Navbar color="light" light expand="md">
<NavbarBrand href="/">TestBrand</NavbarBrand>
<Nav navbar>
<NavItem>
<Link className="lnk" to='/users'>Users</Link>
</NavItem>
</Nav>
<Nav className="ml-auto" navbar>
<NavItem>
<Link className="lnk" to='/logout'>Logout</Link>
</NavItem>
</Nav>
</Navbar>
)
The file which handles all the routes is as below (routes/index.js):
import React from 'react';
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom';
import { AppHeader } from '../components';
export default () => (
<BrowserRouter>
<div>
{
localStorage.getItem('access-token') &&
<div>
<AppHeader />
</div>
}
<Switch>
<Route exact path="/users" component={Users} />
<Route exact path="/users/add" component={Users} />
<Route exact path="/users/:id" component={Users} />
</Switch>
</div>
</BrowserRouter>
)
The main App just contains the following code:
import React, { Component } from 'react';
import Routes from '../routes';
import '../style.css';
class App extends Component {
render() {
return (
<div>
<Routes />
</div>
)
}
}
export default App;
I do not want to refresh the page, as it defeats the very purpose of SPA. How can I achieve that?
Make Routes a stateful Component.
import React from 'react';
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom';
import { AppHeader } from '../components';
class Routes {
this.state = {loggedIn: false}
componentDidMount() {
if(localStorage.getItem('access-token')) {
this.setState({loggedIn: true})
}
//attach an event listener to the window object for storage event.
$(window).on('storage',() => {
if(localStorage.getItem('access-token')) {
this.setState({loggedIn: true})
}
});
}
render() {
return (
<BrowserRouter>
<div>
{
this.state.loggedIn &&
<div>
<AppHeader />
</div>
}
<Switch>
<Route exact path="/users" component={Users} />
<Route exact path="/users/add" component={Users} />
<Route exact path="/users/:id" component={Users} />
</Switch>
</div>
</BrowserRouter>)
}
}
export default Routes;
After the component is mounted you are listening to localStorage updates. if local storage is changed it will update the state of the component as required.

React Link doesn't trigger the router changement

In my react app I've tried lot of different router, route, and solution find on internet.
The fact is i'm using <HashRouter> from react-router-dom and redux in my app.
When I change the url in my browser the right route is triggered and the right component is loaded.
The issue :
When i click on <Link> component the url change, the history props on the router change but nothing happenned in the app...
Here are my app architecture and code :
MainApp.jsx
render(){
<Provider store={store}>
<HashRouter>
<div className="main-app">
<StickyContainer>
<Header toggleHelp={() => this.toggleOverlay()} />
<Sticky>
<Toolbar /> //Here are my <Link/>
</Sticky>
<App/>
<Footer />
</StickyContainer>
</div>
</HashRouter>
</Provider>
}
App.js
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as Actions from 'Actions';
import Main from 'Components/Main/Main';
import {withRouter} from 'react-router-dom';
const App = ({elements, actions,documents,filters}) => (
<div>
<Main elements={elements} actions={actions} documents={documents} filters={filters} />
</div>
)
const mapStateToProps = state => ({
elements: state.elements,
documents: state.documents,
filters:state.filters
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(Actions, dispatch)
});
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(App));
And finally my Main.jsx
render(){
<div className="main-authenticated">
<Switch>
<Route path="/home" component={Home} />
<Route path="/reporting" component={Reporting} />
<Route path="/about" component={About} />
<Route path="/disconnect" component={ErrorPage} />
</Switch>
</div>
}
I already tried with a BrowserRouter, a basic Router with history but always this issue. Don't know if it's due to my project architecture or something else.
UPDATE
Moved withRouter on Main.jsx and got the same issue.
Main.jsx
render(){
<div className="main-authenticated">
<Switch>
<Route path="/home" component={Home} />
<Route path="/reporting" component={Reporting} />
<Route path="/about" component={About} />
<Route path="/disconnect" component={ErrorPage} />
</Switch>
</div>
}
export default withRouter(Main)
As #ShubhamKhatri said, I needed to export my Main component with withRouter function from react-router.
But there was an other issue, the Link included in the Toolbar component was not triggered the router due to the Sticky Component from react-sticky.
Remove the Sticky component wrapper on the MainApp correct the problem.
Final solution :
exporting Main.jsx
class Main
[...]
export default withRouter(Main);
MainApp.jsx
<Provider store={store}>
<HashRouter>
<div className="main-app">
<Header />
<Toolbar/>
<App>
<Footer />
</div>
</HashRouter>
</Provider>

Resources