Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useImperativeHandle callback never called (when rendering w/ enzyme) #2039

Closed
2 of 13 tasks
rally25rs opened this issue Mar 7, 2019 · 6 comments
Closed
2 of 13 tasks
Projects

Comments

@rally25rs
Copy link

( Cross posting this from facebook/react#15054 )

What is the current behavior?

I have the code:

function Form(props, ref) {

  React.useImperativeHandle(ref, () => {
    debugger;
    return {
      setErrors: () => {},
    };
  });
}

export default React.forwardRef(Form);

When I use the component, the callback passed to useImperativeHandle is never called. (The debugger statement is never hit).

The code that I have using the component is:

import {mount} from 'enzyme';

describe('Form component', () => {
  test('exposes a ref', async () => {
    let formRef;
    mount(<Form ref={ref => (formRef = ref)}>{() => <span>test</span>}</Form>);
    await pause(500); // this is just a setTimeout to give time for the hooks to run.
    expect(formRef.setErrors).toBeDefined();
  });

Repro Example:

https://codesandbox.io/s/v8rqy75mn5

If you check the console tab, it only logs the message form the callabck once, but the component is rendered twice (or if you comment out the normal react render then it is no longer console.logged)

Environment

API

  • shallow
  • mount
  • render

Version

    "react": "16.8.0-alpha.1",
    "react-dom": "16.8.0-alpha.1",
    "enzyme": "3.8.0",
    "enzyme-adapter-react-16": "1.8.0",

Adapter

  • enzyme-adapter-react-16
  • enzyme-adapter-react-16.3
  • enzyme-adapter-react-16.2
  • enzyme-adapter-react-16.1
  • enzyme-adapter-react-15
  • enzyme-adapter-react-15.4
  • enzyme-adapter-react-14
  • enzyme-adapter-react-13
  • enzyme-adapter-react-helper
  • others ( )

I noticed that when executed through enzyme.mount my component's function Form(props, ref) is called with null as the ref parameter

It looks like this comes from react's

function updateForwardRef(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {

  ...

  var ref = workInProgress.ref;

workInProgress is a FiberNode and workInProgress.ref is null


The initial react-dom.render is called with a WrapperComponent that has a ref set to my inline function in the code, but it seems to get dropped somewhere between there and the actual call to my component.

I suspect this may have something to do with how enzyme is wrapping components, but debugging react-dom isn't real fun 😄


useImperativeHandle is also listed in enzyme's hooks support checklist #2011 so linking that here. I guess support just isn't there yet? 🤷‍♂️

@chenesan
Copy link
Contributor

chenesan commented Mar 7, 2019

Hi @rally25rs have you tried to wrap the code with act() ?

act(function() {
    mount(<Form ref={ref => (formRef = ref)}>{() => <span>test</span>}</Form>);
    expect(formRef.setErrors).toBeDefined();
})

@rally25rs
Copy link
Author

I just get act is not a function but I suspect that is because I'm on 16.8.0-alpha.1 still which I don't think forced you to wrap things in act.

@ljharb
Copy link
Member

ljharb commented Mar 9, 2019

@rally25rs enzyme doesn't support react alphas, so either way you'll have to upgrade to the actual 16.8 release.

Separately, enzyme doesn't have explicit/official support for hooks at all yet, although that's in progress (see #2011 and related issues).

@ljharb ljharb added this to v16.8+: Hooks in React 16 Mar 9, 2019
@jquense
Copy link
Contributor

jquense commented Apr 1, 2019

This isn't really an issue with hook support directly anyway. mount() "steals" the ref on the root element and applies it to the WrapperComponent instead of the element it's placed on. It's very unclear why it does this.

workaround:

const mountWithRef = (elWithRef, options) => {
  const WithRef = () => el;
  return mount(<WithRef />, options);
};

mountWithRef(<Foo ref={ref} />)

@ljharb
Copy link
Member

ljharb commented Apr 1, 2019

@jquense i think that's #1852?

A PR to fix that is appreciated.

@rally25rs
Copy link
Author

Closing as a duplicate of #1852

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
React 16
  
v16.8+: Hooks
Development

No branches or pull requests

4 participants