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

Error with event bubbling #15

Closed
silvenon opened this issue Jul 26, 2015 · 2 comments
Closed

Error with event bubbling #15

silvenon opened this issue Jul 26, 2015 · 2 comments

Comments

@silvenon
Copy link
Contributor

There's an error happening when a parent of the child which is closing the portal has an onClick handler:

import jsdom from 'jsdom';
import sinon from 'sinon';
import assert from 'assert';

describe('Portal', () => {
  let React, utils, Portal;

  before(() => {
    global.document = jsdom.jsdom('<!DOCTYPE html><html><head></head><body></body></html>');
    global.window = global.document.parentWindow;
    global.navigator = window.navigator;

    React = require('react/addons');
    utils = React.addons.TestUtils;
    Portal = require('react-portal');
  });

  it('handles event bubbling after close', () => {
    const cb = sinon.spy();

    const Component = React.createClass({
      handleClose() {
        this.refs.portal.closePortal();
        console.log('close');
      },

      render() {
        return (
          <Portal ref="portal" openByClickOn={<span ref="toggle" />} onClose={cb}>
            <div onClick={function () { }}> // it works without the handler
              <button ref="close" type="button" onClick={this.handleClose}>Close</button>
            </div>
          </Portal>
        );
      }
    });

    const el = utils.renderIntoDocument(<Component />);

    utils.Simulate.click(React.findDOMNode(el.refs.toggle));
    utils.Simulate.click(React.findDOMNode(el.refs.close));

    assert(cb.called);
  });
});
TypeError: Cannot read property 'firstChild' of undefined

I created a demo where you can run this test.

@tajo
Copy link
Owner

tajo commented Jul 28, 2015

this.refs.portal.closePortal()

This is bad design and it will break with React 0.14 since refs will return underlying DOM nodes and not React components anymore.

There are two other and better ways:

Put

<div onClick={function () { }}> // it works without the handler
  <button ref="close" type="button" onClick={this.handleClose}>Close</button>
</div>

into a new component and then you can use

<button ref="close" type="button" onClick={this.props.closePortal}>Close</button>

or if you don't want to create a new component for some reason, you can use the prop isOpened:

<span onClick={(function(e) {this.setState({isOpened: true})}).bind(this)}>
<Portal isOpened={this.state.isOpened}>
  <div onClick={function () { }}> // should work now
    <button ref="close" type="button" onClick={(function(e) {this.setState({isOpened: false})}).bind(this)}>Close</button>
  </div>
</Portal>

I didn't test the code above, so I'm sorry for possible typos.

@silvenon
Copy link
Contributor Author

Works perfectly, thanks!

This is bad design and it will break with React 0.14 since refs will return underlying DOM nodes and not React components anymore.

This only applies to native DOM components, not custom components:

As with refs, this change does not affect custom components (eg. <MyFancyMenu> or <MyContextProvider>), which remain unaffected by this change.

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

No branches or pull requests

2 participants