7

I am trying to figure out how I can capture all click events to determine if they were clicked outside of my SearchBar drop down menu. If so then the drop down menu will close. I have a way of detecting all click events (TouchableWithoutFeedback) but I am not able to figure out a way to compare or determine if it was outside of my component or not. Anyone know how to do this in React Native??

class Products extends Component {

    constructor(props) {
        super(props);

        this.hideSearchBar = this.hideSearchBar.bind(this);
    }

    hideSearchBar(e) {
        console.log('e: ', e.nativeEvent.target)
        // Determine if the click event was outside of the SearchBar component
    }

    render() {
        const {isLoading, products} = this.props.products;

        return (
            <TouchableWithoutFeedback onPress={(e) => this.hideSearchBar(e)} style={{zIndex: 0}}>
                <View style={styles.wrapper}>
                    <Header/>
                    <View style={styles.bodyWrapper}>
                        <ScrollView style={styles.scrollView}>
                            <ProductsContainer data={{productsList: { results: products }}}/>
                        </ScrollView>
                        <SearchBar ref={node => this.node = node} style={styles.searchBar}/>
                    </View>
                    <Footer/>
                </View>
            </TouchableWithoutFeedback>
        );
    }
}
FairyQueen
  • 2,283
  • 7
  • 37
  • 57
  • Possible duplicate of [React prevent event bubbling in nested components on click](https://stackoverflow.com/questions/38619981/react-prevent-event-bubbling-in-nested-components-on-click) – dave Apr 18 '18 at 20:35
  • 5
    Different environments (react and react-native), how in God's name can it be a duplicate? – Nnanyielugo Aug 31 '18 at 19:06

1 Answers1

3

You can check the clicks based on the PanResponder.

PanResponder contains the methods as mentioned here.

But you only need

  • onMoveShouldSetPanResponderCapture: To set the capture of movement of the interaction.
  • onPanResponderMove: To capture the events on move
  • onPanResponderTerminationRequest: Terminate responder if other resource requires access to it.

Here's the simple example

Note: You need to set the PanResponder Handler on the Parent View to access the touch events in the whole screen.

const touchThreshold = 20;

state = {
        outsideTarget: null,
    }

componentWillMount () {
        this._panResponder = PanResponder.create({   //...Create the Responder
            // Ask to be the responder:
             // Ask to be the responder:
        onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
            const {dx, dy} = gestureState;

            return (Math.abs(dx) > touchThreshold) || (Math.abs(dy) > touchThreshold);
        },
            onPanResponderMove: (evt, gestureState) => {
                console.log('Responder' + evt.nativeEvent.target)
                this.setState({outsideTarget: true})
                // The most recent move distance is gestureState.move{X,Y}

                // The accumulated gesture distance since becoming responder is
                // gestureState.d{x,y}
            },
            onPanResponderTerminationRequest: (evt, gestureState) => true,

        });
    }

componentDidUpdate(prevProps, prevState, snapshot) {
       if(this.state.outsideTarget) {
            Alert.alert('Success', 'Component Clicked OutSide')
        } else if(!this.state.outsideTarget) {
           Alert.alert('Success', 'Component Clicked Inside')

       }
    }


 hideSearchBar(e) {
    // Determine if the click event was outside of the SearchBar component
    this.setState({outsideTarget: false})
}

render() {
    return (
        <View style={{flex: 1}}  {...this._panResponder.panHandlers}> //...Set the responder to the parent view
            <TouchableOpacity  onPressIn={(e) => this.hideSearchBar(e)} style={{height: 100, width: 100, backgroundColor: 'red'}} />
        </View>
    );
}

If the responder's native event target matches the touchable's native event target then it is inside, else outside

Pritish Vaidya
  • 21,561
  • 3
  • 58
  • 76
  • I do not really understand PanResponder but I do not need to track the movement of the mouse I just need to know where the click was made. I was hoping to get an answer that would deal with my hideSearchBar function. I am currently detecting clicks through TouchableWithoutFeedback but determining if the click is outside of a certain component (SearchBar) is what I don't understand how to do. – FairyQueen Apr 19 '18 at 13:34
  • The pan responder is to give you the node id's of the views that have been gestured upon, if the node id is something other than your touchable id then it is outside the touchable box. – Pritish Vaidya Apr 19 '18 at 13:43
  • Which part of your code example is actually doing the evaluation to determine if the click was outside or not?? – FairyQueen Apr 19 '18 at 15:54
  • Perhaps you'll need to add `onStartShouldSetResponderCapture`. The `onStartShouldSetResponderCapture `is called on the beginning touch, and `onMoveShouldSetResponderCapture` is called on every time you move your finger – Pritish Vaidya Mar 22 '19 at 16:15