@jakesidsmith/react-shallow-renderer

A shallow renderer for React components

Usage no npm install needed!

<script type="module">
  import jakesidsmithReactShallowRenderer from 'https://cdn.skypack.dev/@jakesidsmith/react-shallow-renderer';
</script>

README

react-shallow-renderer

A shallow renderer for React components

CircleCI

About

This is an alternative renderer to react-test-renderer/shallow with full support for:

  • React.memo
  • React.forwardRef
  • React.Fragment
  • React.createContext (Provider and Consumer)
  • ReactDOM.createPortal
  • Functional components
  • Component classes

The output of this renderer is far more informative than other existing renderers, providing context of memo wrapped components, fragments, etc.

If you're using jest you may enjoy jest-matcher-react-shallow-snapshot, which wraps this library for ease of use:

expect(<MyComponent />).toMatchReactShallowSnapshot()

Install

npm i @jakesidsmith/react-shallow-renderer -S

Usage

Example with jest:

import React from 'react';
import { ReactShallowRenderer } from '@jakesidsmith/react-shallow-renderer';
import MyComponent from './path';

describe('MyComponent', () => {
  it('renders some stuff', () => {
    const renderer = new ReactShallowRenderer(<MyComponent />);

    expect(renderer).toMatchSnapshot();
  });
});

Newer versions of jest will automatically call the toJSON method of the renderer. If the version you are using doesn't you can try:

expect(renderer.toJSON()).toMatchSnapshot();

Example output in jest snapshots

A form component using memo, Fragment, a SubmitButton component that uses memo, and an external form library that uses forwardRef

import React from 'react';
import { Field } from 'form-library';
import SubmitButton from './path';

const MyComponent = (props) => (
  <>
    <h1>
      Log in
    </h1>
    <form onSubmit={props.handleSubmit}>
      <Field component="input" type="email" name="email"  />
      <Field component="input" type="password" name="password"  />
      <SubmitButton>
        Log in
      </SubmitButton>
    </form>
    <a href="/forgot-password">
      Forgot password?
    </a>
  </>
);

export default React.memo(MyComponent);

The output

<React.Fragment>
  <h1>
    Log in
  </h1>
  <form
    onSubmit={[Function]}
  >
    <React.forwardRef(Field)
      component="input"
      type="email"
      name="email"
    />
    <React.forwardRef(Field)
      component="input"
      type="password"
      name="password"
    />
    <React.memo(SubmitButton)>
      Log in
    </React.memo(SubmitButton)>
  </form>
  <a
    href="/forgot-password"
  >
    Forgot password?
  </a>
</React.Fragment>

A component using ReactDOM.createPortal, and a context consumer

import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
import Popover from './path';
import { MyContext } from './another-path';

export default class MyComponent extends PureComponent {
  render() {
    return ReactDOM.createPortal(
      (
        <Popover>
          <MyContext.Consumer>
            {(value) => (
              <p>
                Some content: {value}
              </p>
            )}
          </MyContext.Consumer>
        </Popover>
      ),
      document.getElementById('my-id')
    );
  }
}

The output

<ReactDOM.Portal>
  <Popover>
    <React.Consumer>
      [Function: Unknown]
    </React.Consumer>
  </Popover>
</ReactDOM.Portal>

You can avoid the Unknown function here by defining a named function, or const outside of the render method, which should give you a nicer output, such as:

<React.Consumer>
  [Function: myFunction]
</React.Consumer>

Tips

In order to get better snapshots (and avoid unknown component names in dev tools), you should not define anonymous / arrow functions in your render method, or immediately inside wrappers like React.memo and React.forwardRef. Instead I recommend the following:

const MyComponent = () => <div />;

export default React.memo(MyComponent);

Or with react-redux:

const MyComponent = () => <div />;

export default connect(mapStateToProps)(React.memo(MyComponent));