PSWai
PSWai

Reputation: 1188

ReactJS: always transfer props to child component

I have a higher-order component that tracks the mouseover event of any composed component. It is roughly like this:

(Component) => class TrackMouseOver extends React.Component {
  handleMouseOver = (e) => {
    console.log('Mouse over!');
  };

  render() {
    return <Component {...this.props} onMouseOver={this.handleMouseOver}/>;
  }
};

However, this does not work very well since React components do not bind to DOM event themselves. Unless the component handle the onMouseOver prop, the HOC above won't work.

Without breaking the black-box nature of React components, is it better to always transfer props to child element? If yes, will there be significant performance impact by doing so?

Upvotes: 2

Views: 355

Answers (1)

Dan Prince
Dan Prince

Reputation: 29999

If you start passing your handler function down, you will be going against the natural flow of events, as they bubble up through the DOM.

Instead, wrap your component in a handling element with a listener and make use of event bubbling.

(Component) => class TrackMouseOver extends React.Component {
  handleMouseOver(e) {
    e.target        // is the element that triggered the event
    e.currentTarget // is the element this handler is attached to
  }

  render() {
    return (
      <div onMouseOver={this.handleMouseOver}>
        <Component {...this.props} />
      </div>
    );
  }
};

When you click on one of the elements inside the new div, the event has a two part lifecycle.

Capturing

The browser works out which top level element the event happened to, then which child of that element and so on until the event finally ends up at an element with no children — the target.

This sequence won't trigger any event listeners (unless they have the useCapture flag set), it just identifies the path of the event.

Bubbling

Now the event's target element has been reached, the event bubbles back up the captured path to the root of the DOM, triggering event listeners as it goes.

This is the reason that event handlers attached to the body element always trigger*, regardless of which element the event happens to. The body is a common ancestor for all elements.

In your case, you can utilise the fact that events for any elements inside your div will eventually bubble their way up to this event handler.

You can work out which element the event came from using the event.target.


* Event handlers won't trigger if an event handler on one of their descendant elements calls event.preventDefault or event.stopPropagation.

Upvotes: 2

Related Questions