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.