React HOC处理给定Component之外的点击

时间:2017-05-04 17:05:23

标签: javascript reactjs higher-order-functions

我希望制作一个高阶组件来管理外部点击。当确定用户已经点击给定组件外部时,应该执行某个传递的功能。该HOC有两个参数:

  • BoundaryComponent :我们感兴趣的组件,用于确定点击是否在其之外
  • onOutsideClick :在组件外部发生点击时执行的功能

ClickOutside 组件如下所示:

import React from 'react'
const { Component } = React
import { findDOMNode } from 'react-dom'

export default function ClickOutside (BoundaryComponent, onOutsideClick) {
    return class Wrapper extends Component {
        constructor (props) {
            super(props)
        }

        componentDidMount() {
            document.addEventListener('click', this.handleClick.bind(this), true)
        }

        componentWillUnmount() {
            document.removeEventListener('click', this.handleClick.bind(this), true)
        }

        render () {
            const props = Object.assign({}, this.props, { ref: this.getContainer.bind(this) })
            return (
                <BoundaryComponent
                    {...props}
                />
            )
        }

        getContainer (wrapped) {
            this.container = findDOMNode(wrapped)
        }

        handleClick(e) {
            if (this.container && !this.container.contains(e.target) && typeof onOutsideClick === 'function') {
                onOutsideClick()
            }
        }
    }
}

我正试图像这样使用这个组件:

import React from 'react'
const { ClickOutside } = 'utils/click-outside'
import { updatePicklistActiveIndex } from 'components/store/actions'
import { getPicklistActiveIndex } from 'components/store/selectors'
import PickList from 'components/picklist'

// ...

function mapDispatchToProps (dispatch) {
    return {
        updatePicklistActiveIndex: (activeIndex) => { dispatch(updatePicklistActiveIndex(activeIndex)) },
    }
}

function mapStateToProps (state) {
    return {
        picklistActiveIndex: getPicklistActiveIndex(state),
    }
}

let onOutsideClick = null // This feels like a code smell

class DropdownPickList extends PickList {
    constructor(props) {
        super(props)
        this.state = {
            dropdownVisible: false,
        }
        onOutsideClick = () => {
            this.props.updatePicklistActiveIndex(-1)
            this.setState({ dropdownVisible: false })
        }
    }

    // ...

    render() {
        return (
            //...jsx to render DropdownPickList
        )
    }
}
const pickList = ClickOutside(DropdownPickList, () => { onOutsideClick() })
export default connect(mapStateToProps, mapDispatchToProps)(pickList)

上述实施正如我所料......

但是在DropdownPickList类的上下文之外定义onOutsideClick,然后在这个类的构造函数中覆盖这个变量引用只是感觉不对。但它似乎是获取关闭下拉列表所需的类中的道具和状态的唯一方法。

我还尝试在DropdownPickList类中使用静态方法来处理onOutsideClick,但是这个问题是这个类作为一个未呈现的组件传递,所以我无法访问我的handleClick中的任何实例方法HOC。

必须有更好的方法吗?任何帮助或替代实施的想法/模式将不胜感激!

1 个答案:

答案 0 :(得分:1)

不是创建函数,只需导出包装器并按以下方式更改:

  • BoundaryComponent:移动到包装器的子节点(所以它实际上是一个包装器),如下所示:

    <Wrapper>
        <BoundaryComponent />
    <Wrapper>
    

在内部包装器中,您将呈现{this.props.children}

  • onOutsideClick:onOutsideClick函数移动到Wrapper的道具:

    <Wrapper onOutsideClick={this.onOutsideClick}>
        <BoundaryComponent />
    <Wrapper>