Access the route in different way in next js - reactjs

I have an application in nextjs. There i built the next route:
<Link
href="colors/[type]"
as={`colors/${type}`}
>
<a>click</a>
</Link>
This route redirect me on /colors/red, or colors/blue and so on, depending by the user. type here is a varible = dynamic element.
Also i have other link:
<Link href="colors">
<a>click to colors</a>
</Link>
This route should redirect me on /colors, but when i click i get error, because the first url colors/red is not equal with this type: colors. So i have to put something after colors to make things happen.
How to solve the issue? and how to make my first route to accept the last parameter as optional.?

The best way to do this is to use dynamic route (more). Start by creating a new page in your /colors folder named [[...type]].js that will catch either /colors or /colors/red. This setup will also catch route like /colors/red/blue.
You can retrieve the variable part of the url by using the next Router (see example below).
import React from 'react';
import Link from "next/link";
import {useRouter} from "next/router";
export default () => {
const router = useRouter()
const { type } = router.query;
return (
<div>
<Link href={'/'}>
Home
</Link>
<Link href={'/colors/red'}>
Red
</Link>
<Link href={'/colors/blue'}>
Blue
</Link>
<div>
{type ? (
`Type: ${type}`
) : (
'no type'
)}
</div>
</div>
);
}

Related

NextJs 13 Experimental App Dir Hash in routes not directing to the hash id

I am using Nextjs 13 with the experimental App Dir but am not sure if this problem I am facing has anything to do with the issue I am facing. I have an id in my home page of "faqs" and when I click on the link, I can see it successfully goes to that link but does nothing in the browser. If I am on another page, I click the link and it takes me to the home page with the correct url but still stays on the top of the page and does not scroll to the indicated id. I did implement scroll={false} as suggested in the documentation but it makes no difference.
Here is a snippet of the relevant code parts:
"use client"
import React, { useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
const Navigation = () => {
const router = useRouter()
...
In the return:
<Link scroll={false} href="/#faqs">FAQS</Link>
I Even tried:
<button type="button" onClick={() => router.push("/#faqs")}>FAQS</button>
In React the hash works fairly well but in next js, even only in client rendering it seems convoluted. If anyone knows what I am doing wrong or if there is a viable work around, I would sure appreciate it.
Thank you in advance.
If I am missing anything, please let me know.
I use hashtags a lot and I plan to start using the app directory in future projects, so I dug into this and it's not pretty. Apparently, NextJS uses a different package for app directory components client-side called "next/navigation". It's very different from "next/router". Also, when using "next/link" elements, NextJS does not trigger the onRouteChangeComplete event when location.hash changes but location.pathname does not.
So, in order to detect a hash change and scroll to the associated element, I finally had to implement this hack:
"use client"
import { Inter } from '#next/font/google'
import paragraph from './paragraph'
import Link from 'next/link'
import { useEffect, useState } from 'react'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
const [navClick, setNavClick] = useState(false);
useEffect(() => {
setTimeout(() => {
const hash = window.location.hash;
if (hash) document.querySelector(hash).scrollIntoView();
}, 0);
}, [navClick])
const toggleNavClick = () => setNavClick((oldVal) => !oldVal);
return (
<main>
<nav>
<ul>
<li>
<Link href="/#one" onClick={toggleNavClick}>Section One</Link>
</li>
<li>
<Link href="/#two" onClick={toggleNavClick}>Section Two</Link>
</li>
<li>
<Link href="/#three" onClick={toggleNavClick}>Section Three</Link>
</li>
</ul>
</nav>
<div className="container">
<section id="one">
<h1>Section One</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="two">
<h1>Section Two</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="three">
<h1>Section Three</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
</div>
</main>
)
}
Since the hash change cannot be detected because no event is triggered, I basically created an event by toggling navClick each time a link is clicked. The navigation logic is enclosed in setTimeout() function because it triggers after window.location is updated.
Repo: https://github.com/designly1/next-hash-test
Demo: https://next-hash-test.vercel.app/

How do I redirect to an External Link in react?

I am building a gallery where you click on the image and it will load in a separate component using props, this image is a URL, taken from an array, where the src property is loaded as a background image via CSS. My challenge is connecting the src data to the child component. See original question
I have found a solution to pass the data using the Link component. Now the URL string is being read like this: http://localhost:3000/https://photos.smugmug.com/photos....
As you can see there is an address within the address in the string.
I have tried changing the state of the URL string but did not work.
My question, how do I write a redirect to fix the HTTP address removing the localhost address
UPDATE
Many thanks to Taylor, Drew, and Ajeet for all of your help!
The solution is posted below, the main issue was I needed a function in the Image component to connect the src props from the GalleryContainer component.
I also changed all "a tags" to "Link components" to keep consistency. More details are in the explained solutions from Drew and Taylor, and also Ajeet code box here
Issues
I don't know why but you don't seem to use Link components consistently in your app; when using anchor (<a>) tags these types of links will reload the page and your app. A similar issue occurs when you manually set the window.location.href.
The Image wasn't correctly accessing the passed route state.
Solution
App
Reorder your routes from more specific to least specific, and remove the link from within the Switch component, only Route and Redirect components are valid children.
function App(props) {
return (
<>
<Router>
<Switch>
<Route path="/gallery" component={GalleryList} />
<Route path="/image" component={Image} />
<Route path="/" component={Home} />
</Switch>
</Router>
</>
);
}
Home
Use Link component to enter the gallery.
import { Link } from "react-router-dom";
...
<Link to="/gallery">
<h4>Click Here to Enter Gallery!</h4>
</Link>
GallerayList
Use Link component for the link back home.
import { Link } from "react-router-dom";
...
<Link to="/">Home</Link>
GalleryContainer
Refer to image source consistently, i.e. src. Pass along also the image id in route state, using a Link.
const GalleryConatiner = (props) => {
return (
// generates the gallery list!
<ul>
<li className={styles["gallery-list"]}>
<Link
to={{ pathname: "/image", state: { id: props.id, src: props.src } }}
>
<div
className={styles["div-gallery"]}
style={{
backgroundImage: `url(${props.src})`,
height: 250,
backgroundSize: "cover"
}}
></div>
</Link>
</li>
</ul>
);
};
src/Public/Image
Use a Link for the link back to the gallery. Use the useLocation hook to access the passed route state.
import { Link, useLocation } from 'react-router-dom';
const Image = (props) => {
const { state: { id, src } = {} } = useLocation();
return (
<section>
<h1 className={styles["h1-wrapper"]}>Image :{id}</h1>
<div className={styles.wrapper}>
<Link to="/gallery">BACK TO GALLERY</Link>
<ImageContainer id={id} key={id} src={src} />
</div>
</section>
);
};
src/Public/ImageContainer
It isn't clear what your plan is for this component and clicking on the div rendering the passed image as a background so just remove the window.location.href logic with history.push if you want to navigate elsewhere. You can access the history object via the useHistory React hook.
Demo
The disconnect is between the GalleryContainer and Image components. In order to access data from the <Link to=...> within the next component, you need to use props.location.propertyName.
So for example, your GalleryContainer needs to link like this:
<Link to={{ pathname: "/image", src: props.src }}>
And then the value can be retrieved inside the Image component like so:
<ImageContainer id={props.id} key={props.id} src={props.location.src} />
You can use
<Link to={{ pathname: "/image", state: { url: props.src } }}>
but then you would have to access it in the linked component like this: props.location.state.url
From there, you can use an <a> tag with an href to link to the src property.
You can simply use a tag to redirect.
<a target='_blank' href={}>
Link
</a>
Remove target attribute if you dont need to open in new tab.

How can i use react-router Link efficiently?

when I use the Link in the navbar in react it takes me to the suggested URL again and again even if I am present at that home ,how can I redirect conditionally such that if I am on the home page then after clicking the home button again, I will not go to the home page again
<Link className="link" to="/leaderboards">
<Home/>
</Link>
Use <NavLink /> component to redirect to other routes and set the active className as disabled class.
App
<NavLink to="/" className="link" activeClassName="disabled-link">Home</NavLink>
CSS
.disabled-link {
pointer-events: none;
}
That's not how you use a Link. You just specify a pathname in a Link component, and manage your paths-components inside a Router.
Now you just render a <Home /> component always, hence the issue.
The open Github issue (at time of writing) for this is here: https://github.com/ReactTraining/react-router/issues/5362
To reiterate the solution here, you can create a hook for to prevent similar locations from being pushed consecutively:
import { useHistory } from "react-router-dom";
export default function useLocationBlocker() {
const history = useHistory();
useEffect(
() => {
history.block(
(location, action) =>
action !== "PUSH" ||
getLocationId(location) !== getLocationId(history.location)
);
},
[] // eslint-disable-line react-hooks/exhaustive-deps
);
}
function getLocationId({ pathname, search, hash }) {
return pathname + (search ? "?" + search : "") + (hash ? "#" + hash : "");
}
To reproduce, click the Home link more than once and notice that you can press the back button the same number of times. (https://codesandbox.io/s/r0wXp0Njw?file=/index.js)

Typical differences between routers and links in NextJS

I am new to NextJS. I would like to know the typical variations and use cases between next/router and next/link.
Which one should I use in various scenarios? Which does what? For instance, if I want to load shared components or navigate among pages which are server-side rendered. Also what is the difference if I call a page with a normal 'a' tag or use a link/router to achieve the same.
import { useRouter } from 'next/router'
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>
)
}
export default ActiveLink
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/about">
<a>About Us</a>
</Link>
</li>
</ul>
)
}
export default Home
what is the difference if I call a page with a normal 'a' tag or use a link/router to achieve the same?
The main difference between a normal <a> tag and next/link or next/router is that the latter two are used for client-side transitions. So a normal <a> tag will load/navigate to the page with an http request, while router and link with load the page client-side.
You can use next/router (inside a function in your component) when next/link is not enough, for example if you need to do some operations before render the new component.
So both router and link has similar behaviours, you can choose, based on what your app needs to do, which one to use.
Both, will run data fetching methods (getServerSideProps, getStaticProps, and getInitialProps)
Link is just an abstraction for the router, it is a lot easier to use the Link component than to manually construct the link every time.

react-router connected to redux : works with links but only the URL change when dispatching push

I'm trying to programmatically push an URL to navigate with react-router, redux, and connected-react-router
When clicking on a <Link /> button, it's working great, the URL is changing and the route too.
But when using a dispatch(push(url)), the URL only change and the content is not updated
I've made a minimal example here.
Any help would be really grateful,
Thanks
A lot of anti-pattern code, poor application structured, and mixing of packages is holding your application back.
I rewrote it entirely, here's what I've done:
Reconfigured your application folder's structure to be standard.
Don't mix Router (BrowserRouter) with ConnectedRouter.
Don't place all of your components within the App.js file.
Since the Header is always mounted, you don't need redux, instead you can just use withRouter (it exposes route props to the component).
Your rootReducer is missing a reducer, so I added a dummyReducer that just returns state.
Stick to Link or this.props.history when navigating. For this example, there's no need to use both. Also, you don't need to use ConnectedRouter's push function, because the history is passed as a prop when using withRouter.
Side note: If you want the Header to be a "router" where all route changes pass through here, then you'll need to create an action and a reducer that passes a string and stores it to the redux's store. The Header is then connected to the redux store and updates the route when this string has changed.
Working example: https://codesandbox.io/s/526p7kjqq4
components/Header.js
import React, { PureComponent, Fragment } from "react";
import { withRouter } from "react-router-dom";
class Header extends PureComponent {
goTo = route => {
this.props.history.push(route);
};
render = () => (
<Fragment>
<ul>
<li>
<button onClick={() => this.goTo("/")}> Announcements </button>
</li>
<li>
<button onClick={() => this.goTo("/shopping")}> Shopping </button>
</li>
</ul>
<div>
<button onClick={() => this.goTo("/shopping")}>
Click here to go shopping ! (if you can...)
</button>
</div>
</Fragment>
);
}
export default withRouter(Header);
routes/index.js
import React from "react";
import { Switch, Route } from "react-router-dom";
import Announcements from "../components/annoucements";
import Shopping from "../components/shopping";
export default () => (
<div style={{ padding: "150px" }}>
<Switch>
<Route exact path="/" component={Announcements} />
<Route path="/shopping" component={Shopping} />
</Switch>
</div>
);
components/App.js
import React, { Fragment } from "react";
import Routes from "../routes";
import Header from "./Header";
export default () => (
<Fragment>
<Header />
<Routes />
</Fragment>
);
Here is what you're trying to accomplish: https://codesandbox.io/s/8nmp95y8r2
However, I DO NOT recommend this as it's a bit unnecessary, when history is either already passed as a prop from the Route or can be exposed when using withRouter. According to the Redux docs, it's not recommended either. And instead to either use Link or pass the history prop to the redux action creator instead of programmatic navigation through redux state.
containers/Header.js
import React, { PureComponent, Fragment } from "react";
import { connect } from "react-redux";
import { push } from "connected-react-router";
class Header extends PureComponent {
goTo = route => this.props.push(route); // this is equivalent to this.props.dispatch(push(route)) -- I'm just appending dispatch to the push function in the connect function below
render = () => (
<Fragment>
<ul>
<li>
<button onClick={() => this.goTo("/")}> Announcements </button>
</li>
<li>
<button onClick={() => this.goTo("/shopping")}> Shopping </button>
</li>
</ul>
<div>
<button onClick={() => this.goTo("/shopping")}>
Click here to go shopping ! (if you can...)
</button>
</div>
</Fragment>
);
}
export default connect(
null,
{ push }
)(Header);
After reading the complete thread react-router-redux's push() not rendering new route, I came across this solution you need to use Router with passing down history as prop down to your app and don't use create from multiple files just import it from a common file.
Here is the working codesandbox: push rendering the new route

Resources