DaFunkyAlex
DaFunkyAlex

Reputation: 1969

Next.js: Router.push with state

I'm using next.js for rebuilding an app for server side rendering. I have a button that handles a search request.

In the old app, the handler was this one:

search = (event) => {
    event.preventDefault();
    history.push({
        pathname: '/results',
        state: {
            pattern: this.state.searchText,
        }
    });
}

In the results class, I could get the state date with this.props.location.state.pattern.

So now I'm using next.js:

import Router, { withRouter } from 'next/router'

performSearch = (event) => {
    event.preventDefault();
    Router.push({ pathname: '/results', state: { pattern: this.state.searchText } });
};

In the results class, I use

static async getInitialProps({req}) {
    return req.params;
}

I'm not sure if I have to add this to my server.js:

server.get('/results', (req, res) => {
    return app.render(req, res, '/results', req.params)
})

However, the function getInitialProps throws an error because req is undefined. Long text, short question: how to pass state or params to another page without using GET parameters?

Upvotes: 87

Views: 256909

Answers (8)

Jojo Narte
Jojo Narte

Reputation: 3104

The approach to this has already been updated, for those who are using Next.js > v13.

Instead of passing an object into router.push we would instead just pass a queryString.

Now you would use useRouter together with useSearchParams

The documentation here actually has a very good example

'use client'
 
export default function ExampleClientComponent() {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
 
  // Get a new searchParams string by merging the current
  // searchParams with a provided key/value pair
  const createQueryString = useCallback(
    (name: string, value: string) => {
      const params = new URLSearchParams(searchParams.toString())
      params.set(name, value)
 
      return params.toString()
    },
    [searchParams]
  );
 
  return (
    <>
      <p>Sort By</p>
      <button
        onClick={() => {
          // <pathname>?sort=asc
          router.push(pathname + '?' + createQueryString('sort', 'asc'))
        }}
      >
        ASC
      </button>
 
      {/* OR using <Link> */}
      <Link
        href={
          // <pathname>?sort=desc
          pathname + '?' + createQueryString('sort', 'desc')
        }
      >
        DESC
      </Link>
    </>
  );
}

Then on the receiving component/page you just use useSearchParams

export function SecondPage() {
  const searchParams = useSearchParams();
  const sortValue = searchParams.get('sort');

  return <h1>Sorting by {sortValue}</h1>;
}

Upvotes: 1

Anas
Anas

Reputation: 1835

In Next.js 13 app directory, you can't pass the params like the previous answers, there is no way to do this using Next.js navigation system. however, you can use plain javascript to do this by using history.pushState(state, "", url) you can read more about it here.

Below is an example of how to use it.

First page:

"use client";
import { useRouter, usePathname } from "next/navigation";

export default function First() {
  const router = useRouter();
  const pathname = usePathname();

  function handleClick() {
    history.pushState({ email: "[email protected]" }, "", pathname + "/second");
    router.push("second");
  }

  return <button onClick={handleClick}>Navigate to Second page</button>;
}

Second page:

"use client";
import { useEffect, useState } from "react";

const myState = history.state;//store the state variable outside the component

export default function Second() {
  const [routeState, setRouteState] = useState({});

  useEffect(() => {
    setRouteState(myState);
  }, []);

  return <h1>{routeState.email}</h1>;
}

Note: this approach will only work in client components and the state will disappear once you refresh the browser, so the use cases of this approach will be very limited. If you want a more convenient way to pass the data you can use query params or localstorage

Upvotes: 6

JoeyChords
JoeyChords

Reputation: 71

If you're using the App Router features and conventions that were introduced in Next.js 13, most of the previous answers to this question do not apply or work at all. For instance, the pathname string has been removed and replaced by usePathname(), and the query object has been removed and replaced by useSearchParams().

This page has the answers you're looking for: https://nextjs.org/docs/app/api-reference/functions/use-router

Upvotes: 1

Anurag Tripathi
Anurag Tripathi

Reputation: 1094

I would like to add in @Ahmet Firat Keler answer. If you want make your URL to remain clean and pass the data as well, we can do it using "next/link".

as props works fine with next/link and you can hide query parameters with next/link as well.

<Link href={`/blogs/${item.slug}?id=${item.id}`} as={`/blogs/${item.slug}`} passHref>
    <a href="" className="hover:text-cyanBlue">
        Read More
    </a>
</Link>

But keep in mind, this won't work if you set target="_blank" to anchor tag or open link to the next tab of the browser

Upvotes: 2

RukshanJS
RukshanJS

Reputation: 966

I don't know whether this supports SSR, but I had to do it as follows to avoid the error cannot read property 'query' of undefined.

This uses useRouter hook to get access to the url, imported as below.

import { useRouter } from 'next/router'

Assume you want to pass data {name:'Someone'} from Component A to Component B.

In Component A,

const router = useRouter();

router.push(
  { pathname: "/path_of_component_b", query: { name: "Someone" } },
  "path_of_component_b"
);

In Component B,

const router = useRouter();

useEffect(() => {
  alert(router.query.name); // Alerts 'Someone'
}, [router.query]);

Upvotes: 21

Ahmet Firat Keler
Ahmet Firat Keler

Reputation: 4065

If you want your url remain clean, make a small addition to Prithwee Das's answer like below.

Router.push({
    pathname: '/about',
    query: { name: 'Someone' }
}, '/about');

Now you can access props in your component using props

...

const YourComponent = (props) => {
    useEffect(() => {
        console.log(props.router.query.name);
    }, [props.router.query]);

    return (
        <React.Fragment>
            ...
        </React.Fragment>
    );
};

...

Upvotes: 44

Aleks
Aleks

Reputation: 984

If you want 'clean' urls, one way to go about it is to add onClick handler to your link and store required information in context/redux store. It easy to implement if you already have one.

<Link href='...'>
  <a onClick={()=>{dispatch(....)}}>Link<a/>
<Link>

Upvotes: 9

Prithwee Das
Prithwee Das

Reputation: 5246

In next.js you can pass query parameters like this

Router.push({
    pathname: '/about',
    query: { name: 'Someone' }
})

and then in your next page (here in /about page), retrieve the query via the router props, which needs to be injected to Component by using withRouter.

import { withRouter } from 'next/router'

class About extends React.Component {
  // your Component implementation
  // retrieve them like this
  // this.props.router.query.name
}

export default withRouter(About)

Upvotes: 125

Related Questions