I have a Next.js page that fetches data in the getInitialProps
function and I want to reload this data from the browser without reloading the page, which would be unperformant and lose the user's scroll position. Is there some way to reload the initial props without reloading the entire page?
I have a Next.js page that fetches data in the getInitialProps
function and I want to reload this data from the browser without reloading the page, which would be unperformant and lose the user's scroll position. Is there some way to reload the initial props without reloading the entire page?
2 Answers
Reset to default 17This can be done by calling router.replace
:
import { useRouter } from 'next/router';
function YourPage({ someProps }) {
const router = useRouter();
// Call this function when you want to refresh the data
const refreshData = () => router.replace(router.asPath);
// Your JSX
}
router.replace
is a method to update the URL without adding a new entry to the history. router.asPath
is the current URL. So it's like doing a client-side redirect to the same page, and client-side redirects re-fetch the props.
The answer above won't work, I don't believe, since functions can't be serialized and sent from server to client.
You could use a higher order component that stores the last initial props as state and can reload those by calling getInitialProps
and setting the state to its return value. Here's a HOC that would do that:
import { NextPage, NextPageContext } from 'next';
import React, { useState, PropsWithChildren, ComponentType } from 'react';
/**
* Removes never-used context values to reduce bloat. Context values may come from server but then
* be used client-side because they are saved in initial props.
*/
function minifyContext(context) {
return { ...context, req: undefined, res: undefined };
}
const withSoftReload = Page => {
async function getInitialProps(ctx) {
return { context: minifyContext(ctx), ...(await Page.getInitialProps(ctx)) };
}
const omitContextFromProps = ({
context,
...props
}) => props;
const NewPage = props => {
// set inner page initial props to wrapper initial props minus context
const [initialProps, setInitialProps] = useState(omitContextFromProps(props));
async function softReload() {
setInitialProps({ children: null, ...(await Page.getInitialProps(props.context)) });
}
return (
<Page
{...{ ...initialProps, softReload }}
/>
);
};
NewPage.getInitialProps = getInitialProps;
NewPage.displayName = `withSoftReload(${Page.displayName})`;
return NewPage;
};
export default withSoftReload;
In your pages you would use it like this:
const MyPage = ({ data, softReload }) => (
<div>
{data}
<button onClick={softReload}>Refresh</button>
</div>
);
MyPage.getInitialProps = async (ctx) => {
// fetch data
};
export default withSoftReload(MyPage);