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

Expose instance of ReactDOM.render/hydrate #1029

Closed
Filipoliko opened this issue Mar 29, 2022 · 2 comments
Closed

Expose instance of ReactDOM.render/hydrate #1029

Filipoliko opened this issue Mar 29, 2022 · 2 comments
Labels

Comments

@Filipoliko
Copy link

Describe the feature you'd like:

Simply expose the instance of component returned by ReactDOM.render/hydrate (/~https://github.com/testing-library/react-testing-library/blob/main/src/pure.js#L61-L63).

This has been proposed in several issues, but always argued against it (#492, #58, #65) and there are some workarounds proposed. I understand the reasoning, but I have yet another scenario, where we could benefit from this and I couldn't find any viable workaround.

I would like to use React Testing Library for our integration tests setup. We have a plugin, which makes it possible to boot whole IMA.js react web application into JSDOM and allows to perform tests against it. Since testing JSDOM elements is not very comfortable without any utilities library, we decided to "mock" ReactDOM and implemented the original ReactDOM methods using Enzyme, which in the end uses wrapped ReactDOM methods, but it allows us to use its nice utilities. Since Enzyme has not seen much development in past few years, we are considering to move to some alternative, which led us here. I tried to implement "mocked" ReactDOM version using React Testing Library, which I almost succeeded, but I am missing one small final piece and that is the returned render/hydrate instance. Without it, I cannot make the integration test setup work, with it I was able to make all the integration tests succeed. We need to access this instance, since the IMA.js application actually saves the results of ReactDOM.render, when rendering a page and later calls setState and even other methods on this component/page.

I feel like this use-case is a bit different compared to the other ones in the issues mentioned above and I couldn't find any proper workaround using the current React Testing Library API. I don't think that this goes against React Testing Library principles, but I can see, that this is quite different, but hopefully still valid, testing scenario.

Assuming, that React Testing Library would return the component instance, then the implementation of ReactDOM using React Testing Library could look something like this.

import { render } from '@testing-library/react';
import ReactDOM from 'react-dom';

class TestingLibraryReactDOM {
    constructor() {
        this._instances = [];
    }

    get __instances() {
        return this._instances;
    }

    render(element, container, callback = () => {}, internalOptions) {
        const renderResult = render(element, { container, ...internalOptions });

        this._instances.push({ container, renderResult });

        callback();

        return renderResult.instance;
    }

    hydrate(...args) {
        return this.render(...args, { hydrate: true });
    }

    unmountComponentAtNode(container) {
        const instance = this._dropInstanceAtNode(container);

        if (!instance) {
            return false;
        }

        instance.renderResult.instance.unmount();

        return true;
    }

    findDOMNode(...args) {
        // eslint-disable-next-line react/no-find-dom-node
        return ReactDOM.findDOMNode(...args);
    }

    createPortal(...args) {
        return ReactDOM.createPortal(...args);
    }

    _dropInstanceAtNode(container) {
        const instanceIndex = this._getInstanceContainerIndex(container);

        if (!~instanceIndex) {
            return null;
        }

        const instance = this._instances[instanceIndex];

        this._instances.splice(instanceIndex, 1);

        return instance;
    }

    _getInstanceContainerIndex(container) {
        return this._instances.findIndex(
            instance => instance.container === container
        );
    }
}

Suggested implementation:

Extend renderResult object with instance key to access the components instance.

Describe alternatives you've considered:

  1. Accessing the instance from DOM, like in this stackoverflow post, but it is very ugly and fragile solution, which can break down with a new version of React.
  2. Using just DOM Testing library, which is still a viable option, but with React Testing Library we would benefit from React specific features.
  3. Overriding ReactDOM.render/hydrate methods to save their results when called. But again, this is very ugly solution and seems unnecessary, since ReactDOM provides these results by default, so why not just send these results?

Teachability, Documentation, Adoption, Migration Strategy:

@eps1lon eps1lon added the enhancement New feature or request label Mar 29, 2022
@eps1lon
Copy link
Member

eps1lon commented Apr 13, 2022

Could you update this proposal with the new createRoot API of React in mind? ReactDOM.render is deprecated so we won't be working on any issues relating to it.

@eps1lon
Copy link
Member

eps1lon commented May 21, 2022

We won't be working on proposals related to testing React 17 and earlier anymore. If this issue is updated with React 18 in mind we can re-open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants