简体   繁体   中英

React child component lose ref of his parent after componentWillUnmount

I got button component which has another child component to show tooltip. I pass ref to <Tooltip> component where I attach event listener to mouseEnter and mouseLeave event to my button.

<Button
    ref={this.buttonRef}
    type={this.props.type}
    className={this.props.className}
    disabled={this.props.operationIsActive || (this.props.enabled == undefined ? undefined : !this.props.enabled)}
    onClick={this.props.onClick}
    onSubmit={this.props.onSubmit}
    icon={this.props.icon}
>
    {this.props.children}
</Button>
<Tooltip domRef={this.buttonRef} text={this.props.tooltip} />

Here is my Tooltip component

export class Tooltip extends ComponentBase<TooltipProps, TooltipState> {
private documentRef = null;

public componentDidMount() {
    super.componentDidMount();
    this.documentRef = this.props.domRef
    if (this.props.domRef) {
        const dom = this.props.domRef.current;
        dom.element.addEventListener("mouseenter", this.showTooltip);
        dom.element.addEventListener("mouseleave", this.hideTooltip);
    }
}

public componentWillUnmount() {
    if (this.documentRef) {
        const dom = this.documentRef.current;
        if (dom) {
            dom.element.removeEventListener("mouseenter", this.showTooltip);
            dom.element.removeEventListener("mouseleave", this.hideTooltip);
        }
    }
}

private showTooltip = (): void => {
    this.updateState({ isTooltipVisible: true })
}

private hideTooltip = (): void => {
    this.updateState({ isTooltipVisible: false })
}

private getStyles(position: any): React.CSSProperties {
    const css: React.CSSProperties = {
        left: position[0].left,
        top: position[0].top + 40,
    }
    return css;
}

public render() {
    if (!this.props.domRef.current) {
        return null;
    }
    if(!this.state.isTooltipVisible)
    {
        return null;
    }
    const position = this.props.domRef.current.element.getClientRects();
    const css = this.getStyles(position);
    return ReactDOM.createPortal(<div style={css} className="tooltip">{this.props.text}</div>, document.getElementById(DomRoot) )
}
}

Everything works fine, but when onClick event is fired on button (for example I got button which only set new state to some component and after that simple div will be rendered), componentWillUnmount method is triggered and ref is lost so I cannot remove those two listeners in Tooltip component. Is it possible to unmount children before parent or any other way how I can fix this?

A ref is just { current: ... } object. The purpose of this recipe is to be able to update current property after ref object was passed by reference.

In the code above <Button> is mounted and unmounted before <Tooltip> . The ability of a ref to secretly update current is misused here. Tooltip already relies on that a ref contains a reference to specific component. It isn't expected that a ref will change during Tooltip lifespan, so it shouldn't rely on transient ref. It should be:

public componentDidMount() {
    super.componentDidMount();
    this.reference = this.props.domRef.current;
    if (this.reference) {
        this.reference.element.addEventListener("mouseenter", this.showTooltip);
        this.reference.element.addEventListener("mouseleave", this.hideTooltip);
    }
}

public componentWillUnmount() {
    if (this.reference) {
        dom.element.removeEventListener("mouseenter", this.showTooltip);
        dom.element.removeEventListener("mouseleave", this.hideTooltip);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM