import PropTypes from 'prop-types' import Link from 'next/link' import Router from 'next/router' import { execOnce, warn } from 'next/dist/lib/utils' import exact from 'prop-types-exact' import { format, resolve, parse } from 'url' export const prefetch = async (href) => { // if we're running server side do nothing if (typeof window === 'undefined') return const url = typeof href !== 'string' ? format(href) : href const { pathname } = window.location const parsedHref = resolve(pathname, url) const { query } = typeof href !== 'string' ? href : parse(url, true) const Component = await Router.prefetch(parsedHref) // if Component exists and has getInitialProps // fetch the component props (the component should save it in cache) if (Component && Component.getInitialProps) { const ctx = { pathname: href, query, isVirtualCall: true } await Component.getInitialProps(ctx) } } // extend default next/link to customize the prefetch behaviour export default class LinkWithData extends Link { // re defined Link propTypes to add `withData` static propTypes = exact({ href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired, as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), prefetch: PropTypes.bool, replace: PropTypes.bool, shallow: PropTypes.bool, passHref: PropTypes.bool, scroll: PropTypes.bool, children: PropTypes.oneOfType([ PropTypes.element, (props, propName) => { const value = props[propName] if (typeof value === 'string') { execOnce(warn)(`Warning: You're using a string directly inside . This usage has been deprecated. Please add an tag as child of `) } return null } ]).isRequired, withData: PropTypes.bool // our custom prop }); // our custom prefetch method async prefetch () { // if the prefetch prop is not defined do nothing if (!this.props.prefetch) return // if withData prop is defined // prefetch with data // otherwise just prefetch the page if (this.props.withData) { prefetch(this.props.href) } else { super.prefetch() } } }