6

I am fairly new to testing React components and am struggling to test a ref created with React.createRef. I've read through this great response am not sure that it addresses my issue. componentDidMount called BEFORE ref callback

constructor(props) {
    super(props);
    this.myRef = React.createRef();
    console.log('this.myRef in constructor', this.myRef);
}

This console log returns null and is expected because the component has not yet rendered.

componentDidMount() {
    console.log('this.myRef in component did mount', this.myRef);
  }
    return (
      <div className="tab_bar">
        <ul ref={this.myRef} className="tab__list direction--row" role="tablist">
          { childrenWithProps }
        </ul>
      </div>

The console log returns the ul html element in componentDidMount. This also is expected because the component has rendered.

HOWEVER,

When I am testing this component:

const children = <div> child </div>;

describe('Tab component', () => {
  it('should render', () => {
    const wrapper = mount(<Tab>{children}</Tab>);
    const ulElement = wrapper.find('ul');
    const instance = ulElement.instance().ref;
    console.log('instance', instance);

    expect(wrapper).toMatchSnapshot();
  });
});

My console log statements in my terminal (this.myRef in my constructor and in componentDidMount) both say {current: null} and instance is undefined.

Can anyone please help me understand what is going on? Thank you!

ASG
  • 213
  • 4
  • 9
  • Does this answer your question? [Testing the \`React.createRef\` api with Enzyme](https://stackoverflow.com/questions/54570400/testing-the-react-createref-api-with-enzyme) – skyboyer Oct 30 '19 at 06:42

1 Answers1

2

This is only supported in React 16.3 or later I guess. And should have the 16.3 adapter installed! Check this for the test implementation example which is copied below.

  class Foo extends React.Component {
    constructor(props) {
      super(props);
      this.setRef = createRef();
    }
     render() {
      return (
        <div>
          <div ref={this.setRef} className="foo" />
        </div>
      );
    }
  }

  const wrapper = mount(<Foo />);
  const element = wrapper.find('.foo').instance();
  expect(wrapper.instance().setRef).to.have.property('current', element);

Looking at your code the following fix should probably work. You have to use the name of your ref, 'myRef', when accessing it from the instance.

//Component
export default class Tab extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = createRef();
  }
  componentDidMount() {
    console.log("TAB Component Did Mount, REF is : ", this.myRef);
  }

  render() {
    return (
      <div className="tab_bar">
        <ul
          ref={this.myRef}
          className="tab__list direction--row"
          role="tablist"
        >
          <li>TEST</li>
        </ul>
      </div>
    );
  }
}

//Test
describe("Tab component", () => {
  it("Ref should be available in instance", () => {
    const wrapper = mount(<Tab />);
    const ulElement = wrapper.find("ul");

    expect(ulElement.instance()).not.toBeNull();
    expect(wrapper.instance()).not.toBeNull();

    expect(ulElement.instance()).toBe(wrapper.instance().myRef.current);
  });
});

EDIT : Working Sandbox instance https://codesandbox.io/s/enzyme-instance-3rktt

Make sure your Adapter version and the React version is the same

Dehan
  • 4,818
  • 1
  • 27
  • 38
  • 1
    Thanks. I think the issue was that I didn't have the correct adapter. I couldn't check for sure because can't get that new adapter currently but I reverted back to callback refs and that worked fine. – ASG Mar 25 '19 at 17:30
  • 1
    It seems to still not work. I just verified that the `ref` is still null in `componentDidMount` with versions `"react": "16.10.2"` and `"enzyme-adapter-react-16": "1.15.1"` – JBSnorro Oct 29 '19 at 20:30
  • @JBSnorro Check the answer now – Dehan Oct 30 '19 at 06:36