Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

[WIP] - Portal component experiment #37

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

miroslavstastny
Copy link
Collaborator

Portal component implementation using React Portal

@miroslavstastny miroslavstastny changed the title WIP: Portal component experiment [WIP] - Portal component experiment Jun 25, 2018
@layershifter
Copy link
Collaborator

Why we need portalEl?

An update for Portal proposed in Semantic-Org#2880 looks cleaner to me.

handleTriggerClick = () => {
debug('handleTriggerClick()')
this.props.trigger.props.onClick()
this.setState({ open: !this.state.open })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since open is an auto-controlled, it should be set with this.trySetState(). This method is added by AutoControlledComponent and ensures props and default props are respected.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Also marked the second parameter on AutoControlledComponent.trySetState() as optional.


handleTriggerClick = () => {
debug('handleTriggerClick()')
this.props.trigger.props.onClick()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will throw if the trigger does not have an onClick prop. Lodash provides a method for safely invoking a path to a function that may or may not exist. We also need to pass the original event back on click:

handleTriggerClick = (e) => {
  _.invoke(this.props.trigger, 'onClick', e)
}


handlePortalMouseEnter = () => {
debug('handlePortalMouseEnter()')
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused method?

if (this.state.portalEl) {
return
}
console.log('creating portalEl')
Copy link
Member

@levithomason levithomason Jun 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No log calls. Use debug() instead. You can enable it from the console like so:

localStorage.debug = 'semanticUIReact:portal'
// refresh browser

Note, we should update the debugger namespace in debug.ts to stardust.

})}
{this.state.open &&
this.state.portalEl &&
ReactDOM.createPortal(this.props.children, this.state.portalEl)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's stick with destructuring props and state at the top of our functions. It breaks object references and makes code more readable:

render() {
  const { open, portalEl } = this.state
  // ...
}

// To discuss:
// when to create rootNode? (it is required in render, componentWillMount is deprecated)
// should multiple portals share it? (how would mouseenter/mouseleave on portalEl work then?)
// when to destroy it (it is too early in componentWillUnmount)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain on how we should handle the root node, however, we can use the constructor for a componentWillMount replacement. Could you explain a bit about why componentWillUnmount is too early for removing the mount node?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In componentWillUnmount all the elements which render() created are still in DOM, that means that the mount node still contains the popup.
I was afraid I would confuse React by removing it's elements from DOM directly. But React seems to be fine with that.

<Button
onClick={() => {
console.log('onClick outer')
}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid creating functions in the render method as it is not a best practice. Treat functions as persisted state and hoist them up to methods. This way, they are the same instance between renders and do not void any equality checks between renders.

@levithomason
Copy link
Member

Why we need portalEl?

The new Portal in SUIR does offer an interesting approach. Check the render method. What do you think @miroslavstastny?

@miroslavstastny
Copy link
Collaborator Author

Why we need portalEl?

The new Portal in SUIR does offer an interesting approach. Check the render method. What do you think @miroslavstastny?

Replacing ReactDOM.unstable_renderSubtreeIntoContainer with ReactDOM.createPortal is the only/main motivation.
I was not aware of that PR, will definitely check it.

@miroslavstastny
Copy link
Collaborator Author

Two main takeaways from the Semantic-Org#2880:

  1. <body> used as the portal root - I was also considering this solution but decided not to go this way because of https://stackoverflow.com/questions/49504546/is-it-safe-to-use-reactdom-createportal-with-document-body
  2. attaching mouseenter/mouseleave events to the PortalInner using Ref - that's smart, it can handle multiple simultaneous portals using single parent element.

@layershifter, @levithomason - what's your opinion on using <body> as the root?

@layershifter
Copy link
Collaborator

As default value, it's okay I think. Possible, @levithomason has another opinion.

@miroslavstastny
Copy link
Collaborator Author

Discussed with @levithomason, we will reuse the new implementation from SIUR.

@levithomason levithomason force-pushed the master branch 5 times, most recently from f4eb5e5 to 339716d Compare July 6, 2018 01:28
@levithomason
Copy link
Member

Ping, let's move this to the new repo.

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

Successfully merging this pull request may close these issues.

3 participants