76

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?

DaFunkyAlex
  • 1,859
  • 2
  • 24
  • 36

5 Answers5

113

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)
Prithwee Das
  • 4,628
  • 2
  • 16
  • 28
  • 3
    ```js this.props.router.query.name ``` it's showing -- Type erroe -- cannot read property 'query' of undefined – Hossain Misho Oct 02 '19 at 07:08
  • same issue here, when it push it won't load the page with an error of cannot read property of req.user (this is what i pass). but if i hit refresh it loads – ggnoredo Nov 18 '19 at 12:51
  • 1
    @ggnoredo my guess is you're using `req.user` in `getInitialProps` but when routing is done in client side there is no `req` object. `req.user` only exists when Next.js is rendering in the server. – Prithwee Das Nov 18 '19 at 17:10
  • @PrithweeDas didn't know that. I've switched to query parameter. Thanks a lot! – ggnoredo Nov 18 '19 at 17:29
  • I have a question, Does this state persist when the pages are reloaded? – Rehan Sattar Mar 05 '20 at 20:34
  • if you're talking about query parameters then yes, you'll have a `?name=Someone` in your url and if you refresh still it'll be the same. – Prithwee Das Mar 06 '20 at 06:12
  • 4
    This pollutes the UR. Isn't there a feature like react router? – Umakant Patil May 06 '20 at 07:04
  • 1
    There is no such feature. you can find more about this here https://github.com/zeit/next.js/issues/771 @UmakantPatil – Prithwee Das May 06 '20 at 07:27
  • this seems to work only for single nested fields. What about objects that have multiple nested fields? – ZenVentzi May 10 '20 at 00:10
  • 1
    @ZenVentzi I can't find a feature like that, don't know if a package simulate that. Generally I use local storage to save the objects, and retrieve them when needed. Just make sure that I clean things regularly not eat the all the space. I hope future release put state when page switching are done client side. – KeitelDOG May 13 '20 at 10:53
  • 1
    can i pass object? – Aayush Neupane Jul 08 '20 at 08:33
  • Please update answer for NextJS 13 – Contestosis Feb 09 '23 at 19:02
34

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>
    );
};

...
Ahmet Firat Keler
  • 2,603
  • 2
  • 11
  • 22
18

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]);
RukshanJS
  • 791
  • 1
  • 7
  • 20
9

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>
Aleks
  • 894
  • 10
  • 14
  • 3
    You don't want to put into Redux too many things. If only one or two components are interested in that state, better to avoid Redux or usage of context – Genia May 13 '21 at 08:32
  • Yeah, it's basically a hack. But because sending deep nested data with Next.js Router is hard/impossible, it has some limited uses – Aleks May 14 '21 at 09:02
1

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

Anurag Tripathi
  • 784
  • 1
  • 13
  • 13