React router grab URL segment in component - javascript

I'm using a simple router in React
<Router>
<div>
<Switch>
<Route path="/" component={ Home } exact />
<Route path="/contact" component={ Contact } />
<Route path="/:slug" component={ Post } />
</Switch>
</div>
</Router>
I'm pulling posts from a blog using REST and have a router component named Post for single blog posts. Any Route that doesn't match with home or contact, uses the post component.
How can I get or pass the route slug/url segment in the Post component? For example if the url segment/slug is /some-blog-post-title, I want to retrieve it, preferably using a React Router function/method if it exists.

You can get the parameters in the props.match.params object. To get your :slug parameter you would write props.match.params.slug.
Example
class Post extends React.Component {
componentDidMount() {
this.getPost(this.props.match.params.slug);
}
componentDidUpdate(prevProps) {
if (prevProps.match.params.slug !== this.props.match.params.slug) {
this.getPost(this.props.match.params.slug);
}
}
getPost = slug => {
// ...
};
render() {
return <h2>{this.props.match.params.slug}</h2>;
}
}

If you want to grab urls in a functional component
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import User from './User';
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path='/user/:userName' component={User} />
<Route>
<div>Default page</div>
</Route>
</Switch>
</Router>
);
}
}
export default App;
Inside the functional component you can grab it
import React from 'react';
import { useParams } from 'react-router-dom';
const User = () => {
const { userName } = useParams();
return (
<div>Username: { userName }</div>
);
}
export default User;

Related

Redirecting to pages depending on conditionals in ReactJS

Given is an application with 3 pages:
"Mainpage"
"PrivatPage"
"UserManagementPage"
After successful login the user is redirected to a "PrivatPage". On the "PrivatPage" the user has the possibility to return to the "MainPage" or to go to the "UserManagementPage". The part with the redirecting from the "MainPage" to the "PrivatPage" works.
The code looks like this:
import PublicPage from "./components/PublicPage";
import PrivatePage from "./components/PrivatePage";
import UserManagementPage from "./components/UserManagementPage";
import React, { useState, Component } from "react";
import { connect } from "react-redux";
const mapStateToProps = state => {
return state;
};
class App extends Component {
render() {
const accessToken = this.props.accessToken;
console.log(accessToken);
let workspace;
if (accessToken) {
workspace = <PrivatePage />;
} else {
workspace = <PublicPage />;
}
return <div className="App">{workspace}</div>;
}
}
export default connect(mapStateToProps)(App);
But how do I use the conditionals to get to the "UserManagementPage" ?
if you consider functional components, you could use BrowserRouter as follows with react-router-dom.
If you need to handle authentication, you can f.e. build a custom <PrivateRoute /> component and use this on your protected routes instead of <Route />. I always keep these routes in a separate file and import them in App.js.
Here for demo purposes routes in App.js:
import { BrowserRouter as Router } from "react-router-dom";
// import your page components
// and add everything else you want to add to your component
const App = () => {
return (
<>
<Router>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/private" element={<PrivatePage />} />
<Route path="/public" element={<PublicPage />} />
<Route path="/user" element={<UserManagementPage />} />
</Routes>
</Router>
</>
);
};
export default App;
Adding on to private blocks answer you would then in your components use the
<Redirect to='/your-route' />
You would then create a boolean state variable and once it return true you could redirect immediatley like this (where you are rendering jsx):
render() {
{booleanState && <Redirect to='/your-route' />}
}

Private routes in "react-router-dom": "6.0.0-beta.0"

Please help me
When the Route is not inside <Routes> it gives an error:
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a
When it is inside <Routes> it give an error as:
Error: [Abc] is not a <Route> component. All component children of must be a <Route> or <React.Fragment>
Pls help me to resolve this situation? Or any suggestion.
Tried this but one of the above error in both cases.
As this does not generate an error, however my child component of the private route does not render.
import React from 'react'
import './App.css'
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Header from './components/Header'
import Home from './components/Home'
import Login from './components/Login'
import ProtectedRoute from './components/Helper/ProtectedRoute';
import { UserStorage } from './UserContext';
import User from './components/User';
const App = () => {
return (
<div>
<BrowserRouter>
<UserStorage>
<>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="login/*" element={<Login />} />
<Route path='/conta' element={<ProtectedRoute/>}>
<Route path='/conta' element={<User/>}/>
</Route>
</Routes>
</>
</UserStorage>
</BrowserRouter>
</div>
)
}
export default App;
ProtectedRoute
import React from 'react';
import { UserContext } from '../../UserContext';
import {Routes, Route, Navigate } from 'react-router-dom';
const ProtectedRoute = (props) => {
const { login } = React.useContext(UserContext);
if (login === true) return (
<Routes>
<Route {...props} />
</Routes>
);
else if (login === false) return <Navigate to="/login" />;
else return null;
};
export default ProtectedRoute;
With layout wrapper components like ProtectedRoute that renders nested Route components then you need to ensure it is rendering an Outlet for them to be rendered into.
Outlet
import React from 'react';
import { UserContext } from '../../UserContext';
import {Routes, Route, Navigate } from 'react-router-dom';
const ProtectedRoute = () => {
const { login } = React.useContext(UserContext);
if (login === undefined) return null;
return login
? <Outlet /> // <-- nested Route components rendered here
: <Navigate to="/login" replace />;
};
...
<Route path='/conta' element={<ProtectedRoute/>}>
<Route path='/conta' element={<User/>}/> // <-- rendered into outlet
</Route>

Is function component that uses React-hook can be render inside from a class component

I use react-hook in my all component. Now when I want to render It's inside React_Router BrowserRouter component It's given me an error.
Error Massage: Invalid hook call. Hooks can only be called inside of the body of a function component
If I understand well your problem, I think you could do nested routes with hoo like it.
Here would be your main router for example:
import React from 'react';
import {Switch, Route, withRouter, Link} from "react-router-dom";
import MyComponent from "./MyComponent";
class Main extends React.Component
{
render() {
return (
<div className='main'>
<Switch>
<Route exact path='/test' component={MyComponent} />
</Switch>
</div>
);
}
}
export default withRouter(Main);
And Here would be your routed component with nested route.
import React from 'react';
import {Switch, Route, withRouter} from "react-router-dom";
class MyComponent extends React.Component
{
render()
{
const {path} = this.props.match;
return (
<div className='test'>
<Switch>
<Route path={`${path}/catalog`}>
<div>Route catalog</div>
</Route>
<Route exact path={path}>
<div>Route dashboard</div>
</Route>
</Switch>
</div>
);
}
}
export default withRouter(MyComponent);
I had the same error in the past days. In my case the problem was that i use render prop of Route component instead of component prop
<Route render={FunCompWithHooks} /> {/* wrong */}
<Route component={FunCompWithHooks} /> {/* correct */}

React Router Error: You should not use Route outside of Router

I'm just doing some basic routing in my react app and I've done it this way before so I'm pretty confused to as why it isn't working now.
The error I am getting says: You should not use <Route> or withRouter() outside a <Router>
I'm sure this is super basic so thanks for baring with me!
import React from 'react'
import { Route } from 'react-router-dom'
import { Link } from 'react-router-dom'
import * as BooksAPI from './BooksAPI'
import BookList from './BookList'
import './App.css'
class BooksApp extends React.Component {
state = {
books: []
}
componentDidMount() {
this.getBooks()
}
getBooks = () => {
BooksAPI.getAll().then(data => {
this.setState({
books: data
})
})
}
render() {
return (
<div className="App">
<Route exact path="/" render={() => (
<BookList
books={this.state.books}
/>
)}/>
</div>
)
}
}
export default BooksApp
You need to setup context provider for react-router
import { BrowserRouter, Route, Link } from 'react-router-dom';
// ....
render() {
return (
<BrowserRouter>
<div className="App">
<Route exact path="/" render={() => (
<BookList
books={this.state.books}
/>
)}/>
</div>
</BrowserRouter>
)
}
Side note - BrowserRouter should be placed at the top level of your application and have only a single child.
I was facing the exact same issue. Turns out that i didn't wrap the App inside BrowserRouter before using the Route in App.js.
Here is how i fixed in index.js.
import {BrowserRouter} from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
document.getElementById('root')
);

react router slug compared to part of array

I started to experiment with react router, and dynamic matches.
I wanted to create a function which matches the slug of the URL to a slug in a JSON file.
The error I get:
TypeError: Unable to get property 'slug' of undefined or null reference
I think that the 'Slug' of the url is undefined, but I am not sure on how to fix it.
screenshot of error
my code for routes.js:
import React from 'react';
import Header from './components/header/header.js';
import Home from './components/home/home.js';
import About from './components/about/about.js';
import NotFound from './components/notFound/notFound.js'
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import PostPage from './components/postpage/postpage.js'
import posts from './files/data.json';
class Routes extends React.Component {
render(){
return (
<Router>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Route path="/:slug" component={props => {
const postt = posts.posts.filter (post => props.params.slug === post.slug)
console.log(postt.length)
return <PostPage post={postt} />
} } />
}}/>
<Route component={NotFound} />
</Switch>
</div>
</Router>
);
}
}
export default Routes;
PostsPage.js:
import React from 'react';
import Post from '../post/post.js'
const PostPage = (props) => (
<div>
<Post {...props.post}/>
</div>
);
export default PostPage;
and posts.js:
import React from 'react';
import { Link } from 'react-router-dom';
import './post.css';
class Post extends React.Component {
render(){
return(
<div>
<div >
<h2 className='subTitle'><Link to={`/post/${this.props.slug}`} className='link'>{this.props.title}</Link></h2>
<p className='content'>{this.props.excerpt}</p>
</div>
</div>
);
}
}
export default Post;
If you made it this far thank you for helping
slug variable is given inside match props which you are missing.
<Route path="/:slug" render={props => {
const postt = posts.posts.filter (post => props.match.params.slug === post.slug)
console.log(postt.length)
return <PostPage post={postt} />
} } />
}}/>
Also, do not inline component use a render function instead. From the docs:
When you use component (instead of render or children, below) the
router uses React.createElement to create a new React element from the
given component. That means if you provide an inline function to the
component prop, you would create a new component every render. This
results in the existing component unmounting and the new component
mounting instead of just updating the existing component. When using
an inline function for inline rendering, use the render or the
children prop (below).
https://reacttraining.com/react-router/web/api/Route/render-func
One of the ways you can get this fixed is by using .find() instead of .filter() like this :
const postt = posts.find (post => props.match.params.slug === post.slug)
And then inside your <Router /> make sure to send the rest of {...props} as well :
<Route path="/:slug" component={props => {
const postt = posts.find (post => props.match.params.slug === post.slug)
console.log(postt.length)
return <PostPage post={postt} {...props} />
} } />

Categories