Get URL pathname in nextjs - reactjs

I have a signin page and layout component.Layout component has header.I don't want to show header in signin .and for that I want to get url pathname.based on pathname show the header .
import * as constlocalStorage from '../helpers/localstorage';
import Router from 'next/router';
export default class MyApp extends App {
componentDidMount(){
if(constlocalStorage.getLocalStorage()){
Router.push({pathname:'/app'});
} else{
Router.push({pathname:'/signin'});
}
}
render() {
const { Component, pageProps } = this.props
return (
//I want here pathname for checking weather to show header or not
<Layout>
<Component {...pageProps} />
</Layout>
)
}
}
please help

If you want to access the router object inside any functional component in your app, you can use the useRouter hook, here's how to use it:
import { useRouter } from 'next/router'
export default function ActiveLink({ children, href }) {
const router = useRouter()
const style = {
marginRight: 10,
color: router.pathname === href ? 'red' : 'black',
}
const handleClick = e => {
e.preventDefault()
router.push(href)
}
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
)
}
If useRouter is not the best fit for you, withRouter can also add the same router object to any component, here's how to use it:
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
https://nextjs.org/docs/api-reference/next/router#userouter

You can use asPath property, that will give you the path (including the query) shown in the browser without the configured basePath or locale:
const { asPath } = useRouter()

Suppose the complete URL of a page is 'abc.com/blog/xyz' and the component file name matching with this route is './pages/blog/[slug].js'
useRouter() hook returns a route object, which has two properties to get the pathname.
One is asPath property, and
Another one is pathname property.
asPath property contains pathname extracted from the URL i.e. /blog/xyz
but pathname property contains the pathname of your project directory i.e. /blog/[slug].
Example Implementation
// .pages/blog/[slug].js
import { useRouter } from 'next/router';
const BlogSlug = () => {
const { asPath, pathname } = useRouter();
console.log(asPath); // '/blog/xyz'
console.log(pathname); // '/blog/[slug]'
return (
<div></div>
);
}
export default BlogSlug;

To fully use the SSR out-of-the-box provided by Next.js, you can use the context object provided in getInitialProps and which contains the pathname. You can then pass this pathname to be used as a props by your component.
For example:
class Page extends React.Component {
static getInitialProps({ pathname }){
return { pathname }
}
render() {
return <div>{this.props.pathname === 'login' ? 'good' : 'not good'}</div>
}
}

Might be late but just use router.pathname
function MyComp() {
const router = useRouter();
return (
<a className={router.pathname === '/some-path' ? 'currentCSS' : 'defaultCSS'}>
Some link
</a>
);
}

One cannot access the Router or the useRouter() options to access the current path in app.js file. This is not client side rendered and hence the only way to access you current path would be to pass it from your getInitialProps() or the getServerSideProps() call to your App component, and then access it there to develop your logic based on the current route.

My app needed to have multiple documents, so I also was looking for a way to get the path name, with nextjs, default document
This is a way that I found, which works for me.
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { LandingPage, WithSidePanels } from '../documents'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
console.log(this.props.__NEXT_DATA__.page)
if(this.props.__NEXT_DATA__.page === "/") return <LandingPage />
return (
<WithSidePanels />
)
}
}
export default MyDocument
So this.props.__NEXT_DATA__.page this is going to give you, the path name, "/", or "/contact" or whatever,
from the _document.js :)

For whom who are searching for an example:
import React, { Component } from "react";
import { withRouter } from 'next/router'
class Login extends Component {
constructor(props) {
super(props);
}
onClickHandler = (event) => {
this.props.router.push('/newPage')
}
render() {
return (
<div>
<p>Hello, {this.props.router.pathname}</p>
<button onClick={this.onClickHandler}>Click me!</button>
</div>
);
}
}
export default withRouter(Login);

Probably to avoid use import Router from 'next/router' in nextjs
you may use
import {useRouter} from 'next/router';

Related

Get URL Params (Next.js 13)

I am building a Next.js 13 project with the /app directory. I have a problem - in the root layout, I have a permanent navbar component in which the component is imported from /components/Navbar.jsx. Basically inside the Navbar.jsx, I want to be able to access the slug parameter in url, for ex: localhost:3000/:slug in which I want the slug id. I have already defined a Next.js 13 page.jsx for that slug. But how do I get the slug id in the navbar component. I also don't want to use window.location.pathname because it doesn't change when the page routes to a different slug and only does when I refresh.
I have tried the old Next.js 12 method:
//components/navbar.jsx;
import { useRouter } from "next/navigation";
export default function Navbar () {
const router = useRouter();
const { slug } = router.query;
useEffect(() => {
console.log(slug);
}, []);
return <p>Slug: {slug}</p>
}
However it does not work.
To get the URL parameters in a Server Component in Next.js 13, you can use the searchParams argument of the Page function.
URL
localhost:3000/?slug
page.js
export default function Page({searchParams}) {
return <Navbar slug={searchParams}></Navbar>
}
navbar.js
export default function Navbar(props) {
return <p>Slug: {props.slug}</p>
}
More info: https://beta.nextjs.org/docs/api-reference/file-conventions/page
Another way is to use the hook useSearchParams.
From the documentation:
import { useSearchParams } from 'next/navigation';
export default function Page() {
const searchParams = useSearchParams();
// E.g. `/dashboard?page=2&order=asc`
const page = searchParams.get('page');
const order = searchParams.get('order');
return (
<div>
<p>Page: {page}</p>
<p>Order: {order}</p>
</div>
);
}
Had to use usePathname
import { usePathname } from 'next/navigation';

How can i get a parameter value in React to use it in the url of an api fetch? [duplicate]

I'm trying to load a details view based on a react-router-dom route that should grab the URL parameter (id) and use that to further populate the component.
My route looks like /task/:id and my component loads fine, until I try to grab the :id from the URL like so:
import React from "react";
import { useParams } from "react-router-dom";
class TaskDetail extends React.Component {
componentDidMount() {
let { id } = useParams();
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default TaskDetail;
This triggers the following error and I'm unsure where to correctly implement useParams().
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
The docs only show examples based on functional components, not class based.
Version <= 5:
You can use withRouter to accomplish this. Simply wrap your exported classed component inside of withRouter and then you can use this.props.match.params.id to get the parameters instead of using useParams(). You can also get any location, match, or history info by using withRouter. They are all passed in under this.props
Using your example it would look like this:
import React from "react";
import { withRouter } from "react-router";
class TaskDetail extends React.Component {
componentDidMount() {
const id = this.props.match.params.id;
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default withRouter(TaskDetail);
Simple as that!
import React, { Component } from "react";
import { useParams } from "react-router-dom";
function withParams(Component) {
return props => <Component {...props} params={useParams()} />;
}
class TaskDetail extends React.Component {
componentDidMount() {
let { id } = this.props.params;
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default withParams(TaskDetail);
Since hooks wont work with class based components you can wrap it in a function and pass the properties along:
class TaskDetail extends React.Component {
componentDidMount() {
const { id } = this.props.params;
// ...
}
}
export default (props) => (
<TaskDetail
{...props}
params={useParams()}
/>
);
But, like #michael-mayo said, I expect this is what withRouter is already performing.
Params get passed down through props on the match object.
props.match.params.yourParams
source: https://redux.js.org/advanced/usage-with-react-router
Here is an example from the docs destructing the props in the arguments.
const App = ({ match: { params } }) => {
return (
<div>
<AddTodo />
<VisibleTodoList filter={params.filter || 'SHOW_ALL'} />
<Footer />
</div>
)
}
You can not call a hook such as "useParams()" from a React.Component.
Easiest way if you want to use hooks and have an existing react.component is to create a function then call the React.Component from that function and pass the parameter.
import React from 'react';
import useParams from "react-router-dom";
import TaskDetail from './TaskDetail';
function GetId() {
const { id } = useParams();
console.log(id);
return (
<div>
<TaskDetail taskId={id} />
</div>
);
}
export default GetId;
Your switch route will still be something like
<Switch>
<Route path="/task/:id" component={GetId} />
</Switch>
then you will be able to get the id from from the props in your react component
this.props.taskId
In react-router-dom-v6 you can easily use useParams() in functional components but when it gets to the class component you have to create HOC (higher-order component) because hooks don't support class components:
import { useNavigate, useParams } from "react-router-dom";
export const withRouter = (WrappedComponent) => (props) => {
const params = useParams();
const navigate = useNavigate();
return <WrappedComponent {...props} params={params} navigate={navigate} />;
};
Then export your component from your HOC and give your component as a parameter. like below:
export default withRouter(YourComponentName);
After that you can easily access the url id with this.props.params.id and you can navigate to other components with this.props.navigate("/YourPath")
React Route v5
Query params can be read and processed as JSON using withRouter and queryString as follow:
import React from "react";
import { withRouter } from "react-router";
import queryString from 'query-string';
class MyComponent extends React.Component {
componentDidMount() {
const params = queryString.parse(this.props.location.search);
console.log('Do something with it', params);
}
render() {
return <div>Hi!</div>;
}
}
export default withRouter(MyComponent);
SmujMaiku is rigth!!! His answer works perfectly. This is how work today with react-router v6
enter code here
import React ,{Component} from 'react'
import { useParams } from "react-router-dom";
import PokeDescription from '../components/PokeDescription'
class PokeInfoConteiner extends Component{
render(){
let urlPokemon= "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/"
const {idPokemon} = this.props.params
console.log(idPokemon)
return(
<div>
<PokeDescription pokeImage={`${urlPokemon}${idPokemon}.png?raw=true`}/>
<p>{}</p>
</div>
)
}
}
export default (props) => (
<PokeInfoConteiner
{...props}
params={useParams()}
/>)
in React Router V6 :
import React, {Component} from 'react';
import {useParams} from 'react-router-dom';
/* This is a higher order component that
* inject a special prop to our component.
*/
function withRouter(Component) {
function ComponentWithRouter(props) {
let params = useParams()
return <Component {...props} params={params} />
}
return ComponentWithRouter
}
class TaskDetail extends React.Component {
state={
id : ""
}
componentDidMount() {
this.setState({
id : this.props.params.id
})
}
static getDerivedStateFromProps(nextProps) {
return {
id : nextProps.params.id
}
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
const HOCTaskDetail = withRouter(TaskDetail);
export default HOCTaskDetail;
React Route v6
My friends, I tried to use in class but I failed to find any doc about it. So after many hours of searching and trying hard this is (in function). Now (i.e when I'm writing this post) there is only limited resource about v6. But there are many for <v6.
Here I'm using useState,useEffect,useParams,axios.
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
const Post = () => {
let { post_id } = useParams();
const [posts, setPosts] = useState({ post: null, countSecrets: 0, ui: '' });
useEffect(() => {
if (posts.countSecrets === 0) {
const doAxe = (a) => {
axios.get('https://jsonplaceholder.typicode.com/posts/' + post_id)
.then((res) => {
setPosts(s => ({ ...s, value: res.data }));
doUI(res.data)
// console.log(res.data)
});
}
setPosts(s => ({ ...s, countSecrets: s.countSecrets + 1 }));
doAxe()
}
}, [posts, post_id]);
let doUI = (x) => {
// console.log('x' + x.title)
const finalPost = (x !== null) ? (
<div className="post">
<h4 className="center">{x.title}</h4>
<p>{x.body}</p>
</div>
) : (
<div className="center">Loading posts...</div>
);
setPosts(s => ({ ...s, ui: finalPost }));
}
return (
<div className="container">
{posts.ui}
</div>
);
}
export default Post;
NOTE:
I faced useEffect looping. I prevented it with a key.
HOPE: This may help someone!
Reference:
using useParams
state inside function
preventing loop from useEffect
In react-router-dom v6, there is no hook such as withRouter therefore my advice to you is to convert your class-based component to a functional component to use useParams hook in your component otherwise you can create a higher-order component to pass your class-based component.
as you know the useParams() is a hook for react-router-dom.
you can not use this inside the componentDidMount() or useEffect() because both of them are method that called during the Mounting phase of the React Life-cycle i.e after the component is rendered.
you have a solution:
create or define another function outside the componentDidMount() to define useParams then call it inside the componentDidMount.
know every thing will be ok.
This is my working example. :)
import React, { Component } from "react";
import { useParams } from "react-router-dom";
function withParams(Component) {
return (props) => <Component {...props} params={useParams()} />;
}
class ProductDetails extends Component {
handleSave = () => {
// Navigate to /products
};
render() {
return (
<div>
<h1>Product Details - {this.props.params.id}</h1>
<button onClick={this.handleSave}>Save</button>
</div>
);
}
}
export default withParams(ProductDetails);
Hooks only work on functional components,
you have to make that ocmponent a functional component
Fixed by creating a wrapping function
I needed to pass params to my SaxjaxApp.js from index.js using react-router-dom v6.
In v6 Switch has been changed to Routes
I got the useParams working with a class component by following Mohamed MAZEK's idea in post 20 using a wrapping function.
I needed to access the sessionId part of the url when it was available.
ie in localhost:3000/shared/123XYZId
I needed the 123XYZId part.
make note of this line : <Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} /> in the index.js below.
:sessionId denotes that useParams has a property called sessionId, that can be accessed by:
const {sessionId} = useParams() from a functional component.
In my index.js file I did this:
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./styles/style.scss";
import SaxjaxAppWrapper from "SaxjaxAppWrapper";
import SaxjaxApp from "./SaxjaxApp";
const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
//INFO: to learn about react-roue-dom v6 https://reactrouter.com/en/v6.3.0/upgrading/v5
root.render(
// <React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} />
<Route path="/" element={<SaxjaxApp />} />
</Routes>
</BrowserRouter>
// </React.StrictMode>
);
This line <Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} /> calls my wrapping function, whereas the default path / just calls the class component.
I had to create a separate file to hold the wrapping function I don't know why:
import React from "react";
import { useParams } from "react-router-dom";
import SaxjaxApp from "SaxjaxApp";
function SaxjaxAppWrapper() {
//I use the params here and store them to pass as props
let { sessionId } = useParams();
return (
//I pass the sessionId from the url params as a prop to my SaxjaxApp class component here
<SaxjaxApp sessionId={sessionId} />
);
}
export default SaxjaxAppWrapper;
My class component:
import React, { Component } from "react";
import "./styles/style.scss";
class SaxjaxApp extends Component {
state = {
octave: 4,
};
constructor(props) {
super(props);
//... initialise stuff
}
//... a lot of methods
render() {
//Access the param here
const { sessionId } = this.props;
<>
<div>
keybordId={sessionId ? sessionId : "no id was passed"}
</div>
</>
);
}
}
export default SaxjaxApp;

NextJS. How do i get canonical URL for dynamic pages?

Before nextjs 9.4 i have been using
next-absolute-url package which gives origin in getInitialProps.
nextsJs 9.5
since with Automatic optimisation we use getServerSideProps and getStaticProps. the package doesn't support these methods and return undefined.
My Question is that if we have dynamic page lets say post/[pid].tsx using the next-seo i require canonical url for it how could i generate the with getStaticProps or getServerSideProps.
There is another question asked with no response here
How to access canonical URL in Next.js with Automatic Static Optimization turned on?
So After Update (Removing Redux from Application) the next-absoulute-url does work on getServerSideProps.
If application state is not too complex React-context API with Hooks work greats. have a read here
https://kentcdodds.com/blog/application-state-management-with-react
this works fine for me
//_app.tsx
import type { AppContext, AppInitialProps } from 'next/app';
import App from 'next/app';
import { appWithTranslation } from 'next-i18next';
import { store } from '##/redux/store';
import { Provider } from 'react-redux';
import Continuous from '#/components/_pages/Continuous';
import GlobalHead from '#/components/_pages/globals/Head';
import Layout from '#/components/Layout';
type IProps = { canonical: string}
class MyApp extends App<IProps> {
static async getInitialProps({ Component, ctx, }: AppContext): Promise<AppInitialProps<any> & IProps> {
const { locale, defaultLocale, asPath } = ctx;
const _locale = locale === defaultLocale ? '' : `/${locale}`;
const _path = asPath === '/' ? '' : asPath;
const host = 'https://focus-hours.com'
return {
pageProps: Component.getInitialProps ? await Component.getInitialProps(ctx) : {},
canonical: `${host}${_locale}${_path}`,
};
}
render(): JSX.Element {
const { Component, pageProps, canonical } = this.props;
return (
<>
<GlobalHead />
<Head>
<link rel="canonical" href={canonical} />
</Head>
<Provider store={store}>
<Layout>
<Component {...pageProps} />
</Layout>
{_isClient && <Continuous />}
</Provider>
</>
)
}
}
export default appWithTranslation<any>(MyApp);
If you need to get the pid from the url, you need to use router.query in the dynamic route page,
which will give you an object like
{"pid":123}
// post/[pid].tsx
import { useRouter } from 'next/router'
getStaticProps(){
const router = useRouter();
const pid = router.query.pid;
return {props:{pid}}
}
I found the perfect documentation for this in this url - https://nextjs.org/docs/routing/dynamic-routes

react-router-dom useParams() inside class component

I'm trying to load a details view based on a react-router-dom route that should grab the URL parameter (id) and use that to further populate the component.
My route looks like /task/:id and my component loads fine, until I try to grab the :id from the URL like so:
import React from "react";
import { useParams } from "react-router-dom";
class TaskDetail extends React.Component {
componentDidMount() {
let { id } = useParams();
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default TaskDetail;
This triggers the following error and I'm unsure where to correctly implement useParams().
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
The docs only show examples based on functional components, not class based.
Version <= 5:
You can use withRouter to accomplish this. Simply wrap your exported classed component inside of withRouter and then you can use this.props.match.params.id to get the parameters instead of using useParams(). You can also get any location, match, or history info by using withRouter. They are all passed in under this.props
Using your example it would look like this:
import React from "react";
import { withRouter } from "react-router";
class TaskDetail extends React.Component {
componentDidMount() {
const id = this.props.match.params.id;
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default withRouter(TaskDetail);
Simple as that!
import React, { Component } from "react";
import { useParams } from "react-router-dom";
function withParams(Component) {
return props => <Component {...props} params={useParams()} />;
}
class TaskDetail extends React.Component {
componentDidMount() {
let { id } = this.props.params;
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default withParams(TaskDetail);
Since hooks wont work with class based components you can wrap it in a function and pass the properties along:
class TaskDetail extends React.Component {
componentDidMount() {
const { id } = this.props.params;
// ...
}
}
export default (props) => (
<TaskDetail
{...props}
params={useParams()}
/>
);
But, like #michael-mayo said, I expect this is what withRouter is already performing.
Params get passed down through props on the match object.
props.match.params.yourParams
source: https://redux.js.org/advanced/usage-with-react-router
Here is an example from the docs destructing the props in the arguments.
const App = ({ match: { params } }) => {
return (
<div>
<AddTodo />
<VisibleTodoList filter={params.filter || 'SHOW_ALL'} />
<Footer />
</div>
)
}
You can not call a hook such as "useParams()" from a React.Component.
Easiest way if you want to use hooks and have an existing react.component is to create a function then call the React.Component from that function and pass the parameter.
import React from 'react';
import useParams from "react-router-dom";
import TaskDetail from './TaskDetail';
function GetId() {
const { id } = useParams();
console.log(id);
return (
<div>
<TaskDetail taskId={id} />
</div>
);
}
export default GetId;
Your switch route will still be something like
<Switch>
<Route path="/task/:id" component={GetId} />
</Switch>
then you will be able to get the id from from the props in your react component
this.props.taskId
In react-router-dom-v6 you can easily use useParams() in functional components but when it gets to the class component you have to create HOC (higher-order component) because hooks don't support class components:
import { useNavigate, useParams } from "react-router-dom";
export const withRouter = (WrappedComponent) => (props) => {
const params = useParams();
const navigate = useNavigate();
return <WrappedComponent {...props} params={params} navigate={navigate} />;
};
Then export your component from your HOC and give your component as a parameter. like below:
export default withRouter(YourComponentName);
After that you can easily access the url id with this.props.params.id and you can navigate to other components with this.props.navigate("/YourPath")
React Route v5
Query params can be read and processed as JSON using withRouter and queryString as follow:
import React from "react";
import { withRouter } from "react-router";
import queryString from 'query-string';
class MyComponent extends React.Component {
componentDidMount() {
const params = queryString.parse(this.props.location.search);
console.log('Do something with it', params);
}
render() {
return <div>Hi!</div>;
}
}
export default withRouter(MyComponent);
SmujMaiku is rigth!!! His answer works perfectly. This is how work today with react-router v6
enter code here
import React ,{Component} from 'react'
import { useParams } from "react-router-dom";
import PokeDescription from '../components/PokeDescription'
class PokeInfoConteiner extends Component{
render(){
let urlPokemon= "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/"
const {idPokemon} = this.props.params
console.log(idPokemon)
return(
<div>
<PokeDescription pokeImage={`${urlPokemon}${idPokemon}.png?raw=true`}/>
<p>{}</p>
</div>
)
}
}
export default (props) => (
<PokeInfoConteiner
{...props}
params={useParams()}
/>)
in React Router V6 :
import React, {Component} from 'react';
import {useParams} from 'react-router-dom';
/* This is a higher order component that
* inject a special prop to our component.
*/
function withRouter(Component) {
function ComponentWithRouter(props) {
let params = useParams()
return <Component {...props} params={params} />
}
return ComponentWithRouter
}
class TaskDetail extends React.Component {
state={
id : ""
}
componentDidMount() {
this.setState({
id : this.props.params.id
})
}
static getDerivedStateFromProps(nextProps) {
return {
id : nextProps.params.id
}
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
const HOCTaskDetail = withRouter(TaskDetail);
export default HOCTaskDetail;
React Route v6
My friends, I tried to use in class but I failed to find any doc about it. So after many hours of searching and trying hard this is (in function). Now (i.e when I'm writing this post) there is only limited resource about v6. But there are many for <v6.
Here I'm using useState,useEffect,useParams,axios.
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
const Post = () => {
let { post_id } = useParams();
const [posts, setPosts] = useState({ post: null, countSecrets: 0, ui: '' });
useEffect(() => {
if (posts.countSecrets === 0) {
const doAxe = (a) => {
axios.get('https://jsonplaceholder.typicode.com/posts/' + post_id)
.then((res) => {
setPosts(s => ({ ...s, value: res.data }));
doUI(res.data)
// console.log(res.data)
});
}
setPosts(s => ({ ...s, countSecrets: s.countSecrets + 1 }));
doAxe()
}
}, [posts, post_id]);
let doUI = (x) => {
// console.log('x' + x.title)
const finalPost = (x !== null) ? (
<div className="post">
<h4 className="center">{x.title}</h4>
<p>{x.body}</p>
</div>
) : (
<div className="center">Loading posts...</div>
);
setPosts(s => ({ ...s, ui: finalPost }));
}
return (
<div className="container">
{posts.ui}
</div>
);
}
export default Post;
NOTE:
I faced useEffect looping. I prevented it with a key.
HOPE: This may help someone!
Reference:
using useParams
state inside function
preventing loop from useEffect
In react-router-dom v6, there is no hook such as withRouter therefore my advice to you is to convert your class-based component to a functional component to use useParams hook in your component otherwise you can create a higher-order component to pass your class-based component.
as you know the useParams() is a hook for react-router-dom.
you can not use this inside the componentDidMount() or useEffect() because both of them are method that called during the Mounting phase of the React Life-cycle i.e after the component is rendered.
you have a solution:
create or define another function outside the componentDidMount() to define useParams then call it inside the componentDidMount.
know every thing will be ok.
This is my working example. :)
import React, { Component } from "react";
import { useParams } from "react-router-dom";
function withParams(Component) {
return (props) => <Component {...props} params={useParams()} />;
}
class ProductDetails extends Component {
handleSave = () => {
// Navigate to /products
};
render() {
return (
<div>
<h1>Product Details - {this.props.params.id}</h1>
<button onClick={this.handleSave}>Save</button>
</div>
);
}
}
export default withParams(ProductDetails);
Hooks only work on functional components,
you have to make that ocmponent a functional component
Fixed by creating a wrapping function
I needed to pass params to my SaxjaxApp.js from index.js using react-router-dom v6.
In v6 Switch has been changed to Routes
I got the useParams working with a class component by following Mohamed MAZEK's idea in post 20 using a wrapping function.
I needed to access the sessionId part of the url when it was available.
ie in localhost:3000/shared/123XYZId
I needed the 123XYZId part.
make note of this line : <Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} /> in the index.js below.
:sessionId denotes that useParams has a property called sessionId, that can be accessed by:
const {sessionId} = useParams() from a functional component.
In my index.js file I did this:
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./styles/style.scss";
import SaxjaxAppWrapper from "SaxjaxAppWrapper";
import SaxjaxApp from "./SaxjaxApp";
const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
//INFO: to learn about react-roue-dom v6 https://reactrouter.com/en/v6.3.0/upgrading/v5
root.render(
// <React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} />
<Route path="/" element={<SaxjaxApp />} />
</Routes>
</BrowserRouter>
// </React.StrictMode>
);
This line <Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} /> calls my wrapping function, whereas the default path / just calls the class component.
I had to create a separate file to hold the wrapping function I don't know why:
import React from "react";
import { useParams } from "react-router-dom";
import SaxjaxApp from "SaxjaxApp";
function SaxjaxAppWrapper() {
//I use the params here and store them to pass as props
let { sessionId } = useParams();
return (
//I pass the sessionId from the url params as a prop to my SaxjaxApp class component here
<SaxjaxApp sessionId={sessionId} />
);
}
export default SaxjaxAppWrapper;
My class component:
import React, { Component } from "react";
import "./styles/style.scss";
class SaxjaxApp extends Component {
state = {
octave: 4,
};
constructor(props) {
super(props);
//... initialise stuff
}
//... a lot of methods
render() {
//Access the param here
const { sessionId } = this.props;
<>
<div>
keybordId={sessionId ? sessionId : "no id was passed"}
</div>
</>
);
}
}
export default SaxjaxApp;

How to redirect to a new page from a function in React?

Right now I have this function in react and I am using it to go back to login and also to check reset the localStorage value for which I am using the function and not since using that I cannot reset local storage value. The function is below:-
logout(){
localStorage.clear();
console.log("cliasdk");
return(
<Redirect to="/login"/>
)
}
This gets executed on clicking a div but I am not able to go to the /login page.How to do it?
If you use the react-router-dom package you can wrap your component with a router and then you have the ability to redirect the user programmatically, like so this.props.history.push('/login').
Eg:
import {withRouter} from 'react-router-dom';
class Component extends React.component {
constructor(props){
}
componentDidMount(){
this.props.history.push('/login');
}
}
export default withRouter(Component);
See: https://www.npmjs.com/package/react-router-dom.
With all previous answers, I'll describe here this use case:
on `/login` page, I would like to go to `/` when login is OK:
Add imports:
import { Redirect } from 'react-router-dom';
Add in your component default state a redirect to false:
state = {
redirect: false,
}
Add to your business logic (ex. onLoginOk()) a change of the redirect state
this.setState({ redirect: true })
Add somewhere in your render root element:
{ this.state.redirect ? (<Redirect push to="/"/>) : null }
That's it.
you can use this example for redirect after rendering a function
import React from 'react';
import { Redirect } from 'react-router-dom';
class MyComponent extends React.Component {
state = {
redirect: false
}
setRedirect = () => {
this.setState({
redirect: true
})
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to='/target' />
}
}
render () {
return (
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
)
}
}
You can use history variable in props or if haven't history in props, you can use withRouter HOC (https://reacttraining.com/react-router/web/api/withRouter)
history.push("/login")
or
history.replace("/login")
This is the simplest if you don't want to deal with react-router-dom.
Here's an example written in react functional components
const Page = () => {
const redirect = () => {
window.location.href = '/anotherPagePath'
}
return (
<button onClick={redirect}>go to another page</button>
)
}
import React from "react"
import { useHistory } from "react-router-dom";
export const Component = ( props ) => {
const history = useHistory()
const handler = () => {
//Redirect to another route
history.push("/route-link")
}
}
Maybe that's what you are looking for.
If you are trying to logout in a React application (that uses the URL pattern /#/page) through a function that clean the local storage / redirect, try to use go:
import { createHashHistory } from "history";
const history = createHashHistory();
history.go("/login");
The go loads a specific URL from the history list, this can be used to go back in the history, but also to go foward, in this case the 'foward' will use /login and the absolute path to redirect.
Update
On React Router 6 you can use useNavigate to navigate programmatically
In React router 6, redirection looks like this:
const navigate = useNavigate();
const goToLoginPage = () => navigate('/login');
All the code can be seen here:
https://github.com/anmk/redirect/blob/redirect/src/App.js
You can also write a component to do this:
https://github.com/anmk/redirect/tree/redirect_to_component
You can change route programmatically, with history like this:
export default class Logout extends Component {
logout = () => {
this.props.history.push("login");
};
render() {
return (
<div>
<h1>Logout</h1>
<button onClick={this.logout}>Logout</button>
</div>
);
}
}
If you need localStorage.clear();, just put it in logout function. You can see full (working) code example here: https://codesandbox.io/s/py8w777kxj
For future reference, if you're not interested in using React Router you could try the same as I use right now which uses the browser's location (URL):
logout(){
// stuff...
location.href = "/login/"
}
Try this
import React from "react";
const createHistory = require("history").createBrowserHistory;
class Logout extends React.Component {
constructor(props) {
super(props);
let history = createHistory();
history.push("/login");
let pathUrl = window.location.href;
window.location.href = pathUrl;
}
render() {
return (
<div>
</div>
);
}
}
export default Logout;
logout(){
localStorage.clear();
this.setState({redirect: true})
}
//inside Render
render(){
const {redirect} = this.state;
if(redirect){
return <Redirect push to="/login"/>
}
}
You need to import Redirect from react-router-dom, like this:
import { Redirect } from 'react-router-dom';
This is how I solved the problem.
import {useDispatch} from "react-redux";
import useRouter from 'hooks/useRouter'
const {push} = useRouter();
const dispatch = useDispatch();
const logout = () => {
localStorage.clear();
push("/login");
dispatch({
type: LOGOUT_STARTED,
payload: false,
});
};
<... onClick={logout} ..>

Resources