next.js: Cannot read property 'prefetch' and 'push' of null

Bug report

Cannot read property ‘prefetch’ and ‘push’ of null

This bug starts coming when I updated the next version from 9.1.4 to 9.5.2 for both production and dev build.

Describe the bug

Cannot read property ‘prefetch’ of null Cannot read property ‘push’ of null

Uncaught TypeError: Cannot read property 'prefetch' of null
    at prefetch (link.tsx?5e4b:98)
    at eval (link.tsx?5e4b:279)
    at eval (link.tsx?5e4b:60)
    at Array.forEach (<anonymous>)
    at IntersectionObserver.rootMargin (link.tsx?5e4b:51)
link.tsx?5e4b:144 Uncaught TypeError: Cannot read property 'push' of null
    at linkClicked (link.tsx?5e4b:144)
    at onClick (link.tsx?5e4b:314)
    at HTMLUnknownElement.callCallback (react-dom.development.js?61bb:336)
    at Object.invokeGuardedCallbackDev (react-dom.development.js?61bb:385)
    at invokeGuardedCallback (react-dom.development.js?61bb:440)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js?61bb:454)
    at executeDispatch (react-dom.development.js?61bb:584)
    at executeDispatchesInOrder (react-dom.development.js?61bb:606)
    at executeDispatchesAndRelease (react-dom.development.js?61bb:713)
    at executeDispatchesAndReleaseTopLevel (react-dom.development.js?61bb:722)
    at forEachAccumulated (react-dom.development.js?61bb:694)
    at runEventsInBatch (react-dom.development.js?61bb:739)
    at runExtractedPluginEventsInBatch (react-dom.development.js?61bb:880)
    at handleTopLevel (react-dom.development.js?61bb:5803)
    at batchedEventUpdates$1 (react-dom.development.js?61bb:24401)
    at batchedEventUpdates (react-dom.development.js?61bb:1415)
    at dispatchEventForPluginEventSystem (react-dom.development.js?61bb:5894)
    at attemptToDispatchEvent (react-dom.development.js?61bb:6010)
    at dispatchEvent (react-dom.development.js?61bb:5914)
    at unstable_runWithPriority (scheduler.development.js?3069:697)
    at runWithPriority$2 (react-dom.development.js?61bb:12149)
    at discreteUpdates$1 (react-dom.development.js?61bb:24417)
    at discreteUpdates (react-dom.development.js?61bb:1438)
    at dispatchDiscreteEvent (react-dom.development.js?61bb:5881)

To Reproduce

it’s coming when Link Tag is used in the Popover element (https://react-bootstrap-v3.netlify.app/components/popovers/)

 <OverlayTrigger
    overlay={(
      <Popover id="popover-saved-item">
        <Link href="/shopping-cart">
          <a><h5>Your Saved Items wishlist</h5></a>
        </Link>
      </Popover>
      )}
  >
Click Popover
  </OverlayTrigger>

Click on

Expected behavior

the error should not be there.

Screenshots

image

System information

  • Version of Next.js: [e.g. 9.5.2]
  • Version of Node.js: [e.g. 10.16.0]

Additional context

When I add prefetch={false} to link then the prefetch error doesn`t come while rendering but push error is still there on click of the Link

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 15
  • Comments: 28 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I was able to work around the issue by mocking next/link instead of next/router:

jest.mock('next/link', () => ({ children }) => children);

@hems updating .storybook/preview.js to the following worked for our use case

import React from "react"; 
import { RouterContext } from  'next/dist/next-server/lib/router-context';  

export const decorators = [
  (Story) => (
    <RouterContext.Provider value={{
      push: () => Promise.resolve(),
      replace: () => Promise.resolve(),
      prefetch: () => Promise.resolve()
    }}>  
      <Story />
    </RouterContext.Provider>
  ),
];

I had a different error message Error: Uncaught [TypeError: Cannot read property 'catch' of undefined] but also from the <Link>.

I managed to fix it by mocking my router like this:

import React, { ReactElement } from "react";
import { render, RenderResult } from "@testing-library/react";
import { NextRouter } from "next/router";
import "@testing-library/jest-dom";
import { RouterContext } from "next/dist/next-server/lib/router-context";

export const mockRouter: NextRouter = {
  basePath: "",
  pathname: "/",
  route: "/",
  asPath: "/",
  query: {},
  push: jest.fn(),
  replace: jest.fn(),
  reload: jest.fn(),
  back: jest.fn(),
  prefetch: jest.fn().mockResolvedValue(undefined),  // This one fixed it for me
  beforePopState: jest.fn(),
  events: {
    on: jest.fn(),
    off: jest.fn(),
    emit: jest.fn(),
  },
  isFallback: false,
};

export const renderWithRouter = (component: ReactElement): RenderResult => {
  return render(
    <RouterContext.Provider value={mockRouter}>
      {component}
    </RouterContext.Provider>
  );
};

and then in my tests, I call renderWithRouter instead of render.

Any ideas on how to solve this issue on StoryBook ??

We have created a separate issue but it issue got closed as duplicated of this issue

Thank You!

My tests are failing now for more or less each <Link> in Jest tests that i run against “pages”. Just wanted to see if anyone else has starting to see this (again) and having a quick fix.

It happened after an upgrade to 10.0.2 (or 10.0.3), it works fine in 10.0.1. Seems to be alot of talk about prefetching in the changelog so maybe one of those PR is to blame.

To busy to dig into some reproduction at the moment and i will just skip my tests for now, and probably movre these more complicated tests to cypress instead.

I was able to work around the issue by mocking next/link instead of next/router:

jest.mock('next/link', () => ({ children }) => children);

Thank you for solution. A better way of mocking Link in case you want to check href attribute in tests as well:

jest.mock('next/link', () => {
  const React = require('react');
  const { UrlObject } = require('url');

  type Url = string | typeof UrlObject;
  type LinkProps = {
    href: Url;
    as?: Url;
  };

  return ({ children, href }: React.PropsWithChildren<LinkProps>) =>
    React.cloneElement(React.Children.only(children), { href });
});

FYI as of next 10 as attribute is not necessary anymore.

@Clement-Bresson Using import { RouterContext } from 'next/dist/next-server/lib/router-context'; ends up with this error for me:

No router instance found.
You should only use "next/router" inside the client side of your app.

Note: I am using import { render } from '@testing-library/react'; instead of ReactDOM.render... from your example.

+1, experiencing all of the above issues when combining React Testing Library and the Link component. Having to fall back on Cypress, which is suboptimal for integration tests IMO.

@Clement-Bresson Using import { RouterContext } from 'next/dist/next-server/lib/router-context'; ends up with this error for me:

No router instance found.
You should only use "next/router" inside the client side of your app.

Note: I am using import { render } from '@testing-library/react'; instead of ReactDOM.render... from your example.

I’ve been getting this error in my react-testing-library tests with v9.5 where I am using in the RouterContext

...
import { render } from '@testing-library/react';
import Router from 'next/router';
import { RouterContext } from 'next-server/dist/lib/router-context';
...
test('when I click the logo, I am taken to the home page', () => {
  const { getByTestId } = render(
    <RouterContext.Provider value={Router}>
      <Header />
    </RouterContext.Provider>,
  );
...
// trigger Link to be clicked -> "Cannot read property 'push' of null" error

I don’t have the same error with 9.4.4

The solution from @Regaddi to mock next/link fixed my test failures.

The failing tests that were throwing this sort of error was pinning my project at Nextv9.4 so I made a stripped back repo to cover all the router tests I needed for Nextv10.

  • testing Link component (commit)
  • tests with useRouter (commit)
  • programatically calling Router.push() (e.g. in a redux-saga) (commit)

The router mock I ended up with was very similar to @AustinShelby’s:

import { RouterContext } from "next/dist/next-server/lib/router-context";

export const routerMock = {
  basePath: "",
  pathname: "/",
  route: "/",
  asPath: "/",
  query: {},
  push: jest.fn().mockResolvedValue(true),
  replace: jest.fn().mockResolvedValue(true),
  reload: jest.fn(),
  back: jest.fn(),
  prefetch: jest.fn().mockResolvedValue(undefined),
  beforePopState: jest.fn(),
  events: {
    on: jest.fn(),
    off: jest.fn(),
    emit: jest.fn(),
  },
  isFallback: false,
};

...
  render(
    <RouterContext.Provider value={routerMock}>
      <Component />
    </RouterContext.Provider>
  );
...

I experience the same error TypeError: Cannot read property ‘push’ of null

Not sure why though. It seems like sometimes router passed to linkClicked function in null here I will try to dig into it.

[Edit]. I found that the link which triggers the bug, in my case, is located inside a ReactDOM.render (I parse some content and insert some React component in the middle of some elements, maybe I would need to do this differently but that’s another story). And it seems inside this rendered component Next context is indeed not passed (or at least the router). Is there a way to manually give the router context for the next link inside to work ? (I found solution below to this question)

[Edit 2] @juliosampaio @mikerid94 I tried to pass the context as you are doing, but found that error is still hapenning because the Context is not the same. So in the link when router is grabbed from useRouter, it’s still empty. Using RouterContext from next/dist/next-server/lib/router-context instead of from next-server/dist/lib/router-context worked for me. So edited solution working for me:

...  
import Router from 'next/router';  
import { RouterContext } from  'next/dist/next-server/lib/router-context';  
...  
ReactDOM.render(  
  <RouterContext.Provider value={Router}>  
     <Link /> // nextJS link  
   </RouterContext.Provider>  
 someElement  
)
...

// trigger Link to be clicked -> Working

It might help some people

Seeing the same thing as soon as I install Next 9.5.3. For me, calling .simulate('click') on a Next link causes this error, but not on a standard anchor tag. See below:

  it('is a test', () => {
    const testSimulate = jest.fn();

    const link = mount(
      <Link href="/test" as="/test" prefetch>
        <a onClick={testSimulate}>test</a>
      </Link>
    );

    const anchor = link.find('a').at(0);

    act(() => {
      anchor.simulate('click');
    });
    expect(testSimulate).toHaveBeenCalled();
  });

output:

Summary of all failing tests
 FAIL  __tests__/components/utilities/TrackingLink.test.js (10.554s)
   <TrackingLink /> › is a test

    Error: Uncaught [TypeError: Cannot read property 'push' of null]

       97 |
       98 |     act(() => {
    >  99 |       anchor.simulate('click');
          |              ^
      100 |     });
      101 |     expect(testSimulate).toHaveBeenCalled();
      102 |   });

conversely, using a standard anchor tag:

  it('is a test', () => {
    const testSimulate = jest.fn();

    const link = mount(<a onClick={testSimulate}>test</a>);

    act(() => {
      link.simulate('click');
    });
    expect(testSimulate).toHaveBeenCalled();
  });

works fine. I dug into the entire stack trace and can’t find a single call to .push(), which is probably the most baffling part to me.

Will fix it 😃

@eric-burel I don’t know about using Webpack aliases.

What happened when it broke is that a feature was introduced that tries to prefetch any internal url as soon as <Link /> is mounted by default. This used to be disabled by default, and the default was switched if I remember correctly. The issue is that in stories and tests, the <Link /> component is used in isolation, outside the Next.js app, and outside of the Router context which provides the prefetch function.

In any case, I submitted a patch #19857 that was just merged this week and is included in the latest release v10.0.5 yesterday, so upgrading to that version should fix it in both tests and stories. Let me know if that is not the case.

I’ve been getting this error in my react-testing-library tests with v9.5 where I am using in the RouterContext

...
import Router from 'next/router';
import { RouterContext } from 'next-server/dist/lib/router-context';
...
test('when I click the logo, I am taken to the home page', () => {
  const { getByTestId } = render(
    <RouterContext.Provider value={Router}>
      <Header />
    </RouterContext.Provider>,
  );
...
// trigger Link to be clicked -> "Cannot read property 'push' of null" error

I don’t have the same error with 9.4.4

I’m facing the same issue here @mikerid94

I’ve been getting the same issue since v9.5(.0?) but with using sweetalert2-react-content v3.1.0