Search code examples
reactjsreact-routerjestjsenzymereact-router-dom

Jest & react-router-dom: React.createElement: type is invalid -- expected a string (for built-in components) ... but got: undefined


I'm writing tests for a component that has Link from react-router-dom.

The component itself works without giving any error or warning.

However, when the component is rendered with shallow from enzyme, it shows the following error message in the console.

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

As far as I've researched, apparently this error occurs when you wrongly import a React module (or a component).

e.g.

import Link from 'react-router-dom'; // Wrong!
import { Link } from 'react-router-dom'; // Correct!

However, in my case, Link is correctly imported but it still gives the warning message above.

Here's a snippet of the component.

import React from 'react';
import { Link, useHistory } from 'react-router-dom';
// other modules here

const ListItem = (props) => {
  const history = useHistory();
  const queryParameter = _.get(history, 'location.search', '');
  const pathName = `/blah/${props.id}${queryParameter}`;

  return (
    <li>
      <Link to={pathName}>
        <div>blah blah...</div>
      </Link>
    </li>
  );
};

(When I comment-out <Link>, the warning message will be gone from the console. So, using useHistory() should have nothing to do with the warning)

jest.mock('react-router-dom', () => ({
  useHistory: jest.fn()
}));

describe('ListItem', () => {
  const baseProps = { /* initial props here*/ };

  it("renders the name", () => {
    const wrapper = shallow(<ListItem {...baseProps} />);
    // the line above shows the warning message in the console
  });
});

I'm completely clueless. Any advice will be appreciated.


Solution

  • I ran into this issue as well today, and this is the only place I've ever seen with anyone experiencing the same thing. After banging my head against the wall for an hour, I finally figured it out—and indeed, this is probably the cause of your issue as well. It's been 2 years since you asked this so you probably don't need the answer anymore, but for any future developers stumbling across this post...

    The component is importing correctly in practice but not in your tests because of your mock, specifically this part:

    jest.mock('react-router-dom', () => ({
      useHistory: jest.fn()
    }));
    

    This replaces all of the react-router-dom import with an object containing only the property useHistory, so importing Link from it gives you an undefined. It's easily fixed as so:

    jest.mock('react-router-dom', () => ({
      ...jest.requireActual('react-router-dom'),
      useHistory: jest.fn()
    }));