zhuanlan.zhihu.com/p/40811867


Native DOM makes it easy to implement simple Dropdown, but it’s hard to meet our various needs, so a variety of third-party implementations of Dropdown have emerged. Ant-design is a set of UI components implemented based on React. We chose to analyze the Dropdown of the components.

The main components of Dropdown:

  • A pop-up drop-down list
  • A current selection
A few implementation considerations: when to pop up drop-down options, and where to hang them. The intuitive pull-down option should be an absolutely positioned Popup DIV. The difference is whether user events occur inside or outside the drop-down option, since the outside might expect to collapse the drop-down list.

With these questions in mind we start with the react-component/dropdown

External interfaces:

onOverlayClick: func
onVisibleChange: func
animation: any
align: object
placement: string
overlay: node
trigger: array
alignPoint: bool
showAction:
hideAction
getPopupContainer: funcCopy the code
Through these interfaces, we can see that our questions have corresponding answers

Dropdown React node view

Dropdown’s source code uses the Trigger abstract component to implement most of the logic.

The Trigger component is an abstract component that specifies a POPup type UI. Examples include popup and alignment.

Trigger Creates and deactivates popUp

React 16 provides Portal for specifying content to divs that are not in the current node’s hierarchy. And is compatible with the previous version to use ReactDOM. Methods of unstable_renderSubtreeIntoContainer, implementation is relatively complex, specific to see the ContainerRender util.

Listen for events in the Trigger Component’s componentDidUpdate life cycle.

if (state.popupVisible) {
      let currentDocument;
      if(! this.clickOutsideHandler && (this.isClickToHide() || this.isContextMenuToShow())) { currentDocument = props.getDocument(); this.clickOutsideHandler = addEventListener(currentDocument,'mousedown', this.onDocumentClick);
      }
      // always hide on mobile
      if(! this.touchOutsideHandler) { currentDocument = currentDocument || props.getDocument(); this.touchOutsideHandler = addEventListener(currentDocument,'touchstart', this.onDocumentClick);
      }
      // close popup when trigger type contains 'onContextMenu' and document is scrolling.
      if(! this.contextMenuOutsideHandler1 && this.isContextMenuToShow()) { currentDocument = currentDocument || props.getDocument(); this.contextMenuOutsideHandler1 = addEventListener(currentDocument,'scroll', this.onContextMenuClose);
      }
      // close popup when trigger type contains 'onContextMenu' and window is blur.
      if(! this.contextMenuOutsideHandler2 && this.isContextMenuToShow()) { this.contextMenuOutsideHandler2 = addEventListener(window,'blur', this.onContextMenuClose);
      }
      return;
    }Copy the code
Check whether the mouse leaves popup

onPopupMouseLeave = (e) => {
    // https://github.com/react-component/trigger/pull/13
    // react bug?
    if(e.relatedTarget && ! e.relatedTarget.setTimeout && this._component && this._component.getPopupDomNode && contains(this._component.getPopupDomNode(), e.relatedTarget)) {return;
    }
    this.delaySetPopupVisible(false, this.props.mouseLeaveDelay);
  }Copy the code
Is the operation on popup or some other area

onDocumentClick = (event) => {
    if(this.props.mask && ! this.props.maskClosable) {return;
    }
    const target = event.target;
    const root = findDOMNode(this);
    const popupNode = this.getPopupDomNode();
    if(! contains(root, target) && ! contains(popupNode, target)) { this.close(); }}Copy the code

Trigger Alignment

Another thing the Trigger does is locate the popup. This section describes the DOM-align library, which is used to specify the alignment position of DOM nodes. API main interface defines source, target, offset, overflow.