Pass object one page to another page using Link in React Js - javascript

I need to pass object one page to another page. According to requirement, I need to do it using "Link" tag. this is my code.
1st page is as below
<Link
to={{
pathname: "/2ndPage",
state: { foo: 'bar'}
}}
> Action</Link>
When I click this button, I need to pass object to my second page. this is my second page code that I tried
function 2ndPage(props) {
console.log(props)
return <h1>Hello</h1>;
}
correctly project build successfully. When I click my first page button, page is redirect correctly. But object did not pass. I think this is wrong approach. can u help me to do this.

First of all, start the function name with a number it's just a little unconventional.
The foo value should be located in location.state
Try this
import { useParams} from "react-router-dom";
function TwoNdPage(props){
let { foo } = useParams();
//const {foo} = props.location.state // or you can try this
console.log(foo) // it should return bar
return <h2>About</h2>
}
export default TwoNdPage;

Your state will be on location object location.state. You can use hook for location useLocation inside SecondPage component. Attention location.state can be undefined if you open link directly but it has to be present after click on link.
import React from 'react';
import { useLocation } from 'react-router-dom';
const SecondPage = () => {
const location = useLocation();
return (
<p>{location.state}</p>
);
};

Related

How to send a unique id to the next page while navigating using Link in NextJS

I am trying to send an id to the next page when the user navigates.
I have a homepage where I am fetching an Array of data and using .map to display it in a kind of card-based UI.
Now, when the user clicks the card, they will be navigated to the next page display details about that card.
Suppose the home page is here - localhost:3000
And user clicks the card with an id of 234
They will be navigated to the next page as :
The next page is as - localhost:3000/user/234
Now here I want to display information about that card with an id of 234. FOr that I do need to make a fetch request as such fetch(https://userdatabase/234)
The above 234 is a dynamic id for sure, How can I let this fetch request know to change this id every time a new card has been clicked? Or in other words, How this page "knows" the id of the card ?
Right now, I', using a workaround as :
When the user is at the second page, the URL will be like this localhost:3000/user/386
Get this id in NextJS using useRouter as :
import {useRouter} from 'next/router'
`const router = useRouter()`
fetch(`localhost:3000/user/${router?.query?.user})
I understand taking id from URL and making a fresh quest is not ideal at all and this is causing stale caching issue on the second page.
How do I resolve this in a better way?
Thanks so much for reading.
you need to make a dynamic route : Next.js Docs
for your case make a file pages/user/[id].js
1. Client side
access id with this snippet :
import { useRouter } from 'next/router'
const Component = () => {
const router = useRouter()
const { id } = router.query
return <p>{id}</p>
}
export default Component
2. Server side
you can use it any of the data fetching functions
Snippet for SSR :
export async function getServerSideProps({ params }) {
const { id } = params
// fetch data from database with id
return {
props: {}, // will be passed to the page component as props
}
}
More on what gets passed to data fetching functions as Context : Context Parameter
Add id to the dependencies array of useEffect().
Something along these lines:
import { useState, useEffect } from "react";
import { useRouter } from 'next/router';
function Page() {
const router = useRouter();
const [page, changePage] = useState();
// `query` can only be fully parsed client-side
// so `isReady` flag is needed
const { query, isReady } = router;
const { id } = query;
// also need to parse the query value to undefined or string
const parsedID = id === undefined
? undefined
: Array.isArray(id)
? id[0]
: id;
useEffect(() => {
// refuse to run the effect if query is not ready
// or ID is undefined
if (!isReady || !parsedID ) {
return;
}
// this looks ugly
// but pure promise syntax is even uglier
// and `useEffect()` doesn't accept async functions
(async () => {
// errors are assumed to be handled in the `_app` component
// so no error-handling logic here
const response = await fetch(`localhost:3000/user/${parsedID}`);
const newPage = await response.json();
changePage(newPage);
})()
}, [isReady, parsedID]);
return (
<>
{!page
// show loading placeholder until the page is fetched
? <div>Loading...</div>
// pass the `page` value to whatever component you need
: ...
}
</>
)
}
export default Page;

Nextjs: useEffect only once (if I use Link component)

I use Link component for open pages without reload:
<Link href="/home"><a>Home</a></Link>
<Link href="/page"><a>Page</a></Link>
This in my home:
const loadedRef = useRef(false)
useEffect(() => {
if(!loadedRef.current){
console.log("run")
loadedRef.current = true;
}
}, []);
This work fine for first load.
If I click on page and click on home, useEffect run again!
I want only and only once load useEffect even click another pages and return to home
useRef creates a value for this specific instance of the home component. If the instance unmounts, then that value goes away. If you want to make a global variable that persists even after the component has unmounted, do something like this:
import React, { useEffect } from 'react';
// Deliberately making this *outside* the component
let loaded = false;
const Home = () => {
useEffect(() => {
loaded = true;
}, []);
// ...
}
export default Home;
It occurs because the feature "shallow" of next.js
https://nextjs.org/docs/routing/shallow-routing
The official documentation asks to listen to the variable in the query string that receives the request. Generally, this variable is the name of your page like [slug].js. You can inspect the Network tab (F12) to see what is variable used in the query string also.
The below example inspects the slug variable.
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Current URL is '/'
function Page() {
const router = useRouter()
useEffect(() => {
// in my case the page is [slug].jsx
}, [router.query.slug])
}
export default Page

Redirect in an exported function with react-router-dom without passing anything?

I have a very simple logout function that looks like this:
export const logout = () => {
localStorage.removeItem('_id')
localStorage.removeItem('token')
localStorage.removeItem('refresh_token')
return {}
}
I'd like this function to redirect using react-router-dom but I'd also like to avoid passing anything to it. This would allow me to call this function outside the scope of a React Component and would mean I don't have to clutter my application by passing the history object everywhere. I can't use redirect as this function must return an empty object.
In a perfect world I'd be able to import something from react-router-dom at the top of the document that would allow me to programatically redirect from within this function.
If you can import the history object in your react component:
import { useHistory } from 'react-router-dom';
const ReactComponent = () => {
const history = useHistory();
// ... React component to be continued
You can pass it into your function:
// ... React component continued
<button onClick={() => logout(history)}> log out </button>
And push a new route to it:
export const logout = (history) => {
localStorage.removeItem('_id')
localStorage.removeItem('token')
localStorage.removeItem('refresh_token')
history.push('/desiredRoute')
return {}
}
Then if you have a Route component with its path set up as /desiredRoute it should render its children.

Redirect to passing some value to the redirected-to component

I want to pass some value from one component to another once the user clicks a button.
First Component (Survey) with a value survey --> User clicks a button --> Second component (Details) renders the survey
To achieve so I am using Redirect from the react-router-dom.
function Survey({survey}) {
....
// There is a button that triggers the below if clause. The redirect works but it seems the state is not sent.
if (surveyPage === true) {
return <Redirect to={{
pathname: '/detail',
state: { location: survey }
}}
}
return("something")
}
Second component
function Details(){
console.log( this.props.location.state.location) // Cannot read property 'props' of undefined
return "test"
}
Cannot read property 'props' of undefined
I tried also:
function Details({survey){
console.log(survey) // Undefined
return "test"
}
and
function Details({ location: survey }){
console.log(survey) // Undefined
return "test"
}
I am really not understanding what I am doing wrong. I am following the documentations:
https://reacttraining.com/react-router/web/guides/primary-components
this is not accessable in functional components. You need to do it like this:
import { useHistory } from "react-router-dom";
function Details(props){
const history = useHistory();
console.log(history.location.state.location);
return <p>Test</p>
}
export default Details;
Hope this works for you.
To connect your component to the routes information, you should use the withRouter HOC or useHistory hook.
withRouter example:
import { withRouter } from "react-router";
function Details(props){
console.log(props.history.location.state.location);
return <p>Test</p>
}
export default withRouter(Details);

How to get previous url in react gatsby

I am pretty much familiar with the React.js but new to Gatsby.
I want to detect the previous page URL in Gatsby?
You can pass down state using the Link component:
import React from 'react';
import { Link } from 'gatsby';
const PrevPage = () => (
<div>
<Link
to={`/nextpage`}
state={{ prevPath: location.pathname }}
>
Next Page
</Link>
</div>
)
const NextPage = (props) => (
<div>
<p>previous path is: {props.location.state.prevPath}</p>
</div>
);
Then you have access to prevPath from this.props.location.state in the next page.
Full credit to #soroushchehresa's answer — this answer is just extras built upon it.
Gatsby will throw error during production build, since location is not available during server-side rendering. You could get around it by checking for window object first:
class Page extends React.Component {
state = {
currentUrl: '',
}
componentDidMount() {
if (typeof window == 'undefined') return
this.setState({ currentUrl: window.location.href })
}
render() {
return (
<Link to="..." state={{ prevUrl: this.state.currentUrl }}>
)
}
}
But this requires us to implement this on every page, which is tedious. Gatsby has already set up #reach/router for server-side rendering, so we can hook into its location props. Only router components get that props, but we can use #reach/router's Location component to pass it to other components.
With that, we can write a custom Link component that always pass previous url in its state:
// ./src/components/link-with-prev-url.js
import React from 'react'
import { Location } from '#reach/router'
import { Link } from 'gatsby'
const LinkWithPrevUrl = ({ children, state, ...rest }) => (
<Location>
{({ location }) => (
//make sure user's state is not overwritten
<Link {...rest} state={{ prevUrl: location.href, ...state}}>
{ children }
</Link>
)}
</Location>
)
export { LinkWithPrevUrl as Link }
Then we can import our custom Link component instead of Gatsby's Link:
- import { Link } from 'gatsby'
+ import { Link } from './link-with-prev-url'
Now each Gatsby page component will get this previous url props:
const SomePage = ({ location }) => (
<div>previous path is {location.state.prevUrl}</div>
);
You might also consider creating a container that store state for the client side & use the wrapRootElement or wrapPageElement in both gatsby-ssr.js and gatsby-browser.js.
These answers are partially correct. If you set state using link api then the state persists in browser history.
So if you go from Page1 to Page2 then the eg the state.prevUrl will correctly be set to Page1
But if the you go to Page3 from Page2 and then do a browser back then the state.prevUrl will still be Page1 which is false.
Best way I found to deal with this is to add something like this on the gatsby-browser.js
export const onRouteUpdate = ({ location, prevLocation }) => {
if (location && location.state)
location.state.referrer = prevLocation ? prevLocation.pathname : null
}
this way you will have always the previous url available on location.
I resolved my problem with the below piece of code. Here is the ref link https://github.com/gatsbyjs/gatsby/issues/10410
// gatsby-browser.js
exports.onRouteUpdate = () => {
window.locations = window.locations || [document.referrer]
locations.push(window.location.href)
window.previousPath = locations[locations.length - 2]
}
Now you can get previousPath can be accessed from anywhere.

Categories

Resources