TouchOpacity onPress() method not working on child component

45 views Asked by At

I don't have much experience working with React Native so bear with me

I am trying to add an animation to one of my components, a search bar that should change its height when click on. I wrapped my <SearchComponent> component in <TouchOpacity> however the onPress() method doesn't work when I click on the component directly. It only works when I click around the margins

see red border in image

Following the example from this SO post

App.js

import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';


import SearchComponent from './assets/components/SearchComp';
import MapScreen from './assets/screens/MapScreen';


export default function App() {

  const [expanded, setExpanded] = useState(false);

  if (expanded) {
    console.log("dkfjl");
  }

  return (
    <SafeAreaView style={styles.container} >

      <MapScreen />

      <View style={styles.searchContainer} >
        <TouchableOpacity
          onPress={() => {
            setExpanded(!expanded)
          }}>
          <SearchComponent expanded={expanded} />
        </TouchableOpacity>

      </View>

    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  searchContainer: {
    bottom: 0,
    position: 'absolute',
    width: '100%'
  }
})

SearchComponent.js

import React, { useEffect, useState } from 'react'
import { Animated } from 'react-native'
import { TextInput } from 'react-native-paper';


export default function SearchComponent({ expanded = false }) {

    const [search, setSearch] = useState('')

    const [bottom] = useState(new Animated.Value(0))

    useEffect(() => {
        Animated.timing(bottom, {
            toValue: !expanded ? 80 : 10,
            duration: 150,
            useNativeDriver: false
        }).start();
    }, [expanded, bottom]);


    return (
        <Animated.View style={{ bottom, backgroundColor: 'red', borderWidth: 3, borderColor: 'red' }}>
            <TextInput placeholder='Search' defaultValue={search} onChangeText={e => setSearch(e)} style={{
                backgroundColor: 'white', borderTopLeftRadius: 20, borderTopRightRadius: 20, borderBottomLeftRadius: 20, borderBottomRightRadius: 20
            }} />
        </Animated.View>
    )
}

2

There are 2 answers

0
noor.soreti On

I attempted @Vibhor's answer but I was still getting the same problem; pressing directly on the search bar would not trigger an event. Instead I decided to completely remove TouchableOpacity and let the TextInput handle the event

new App.js (as suggested by @Vibhor)

export default function App() {

  const [expanded, setExpanded] = useState(false);

  if (expanded) {
    console.log("dkfjl");
  }

  return (
    <SafeAreaView style={styles.container} >

      <MapScreen />

      <View style={styles.searchContainer} >
        <SearchComponent expanded={expanded} setExpanded={setExpanded} />
        <ExpandedView expanded={expanded} />

      </View>

    </SafeAreaView>
  );
}

updated SearchComponent.js

export default function SearchComponent({ expanded = false, setExpanded }) {

    const [search, setSearch] = useState('')

    const [bottom] = useState(new Animated.Value(0))

    useEffect(() => {
        Animated.timing(bottom, {
            toValue: !expanded ? 80 : 10,
            duration: 150,
            useNativeDriver: false
        }).start();
    }, [expanded, bottom]);


    return (
        <Animated.View style={{ bottom, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', backgroundColor: 'white' }}>
            <Icon name="ios-search" style={{ fontSize: 25, paddingRight: 10 }} />

            // update state in `onPressIn()`
            <TextInput onPressIn={() => setExpanded(!expanded)} underlineColor='transparent' placeholder='Search' defaultValue={search} onChangeText={e => setSearch(e)} style={{
                backgroundColor: 'white', borderTopLeftRadius: 20, borderTopRightRadius: 20, borderBottomLeftRadius: 20, borderBottomRightRadius: 20,
            }} />
        </Animated.View>
    )
}

0
Vibhor On

You are wrapping <SearchComponent/> with TouchableOpacity which is not the right way of doing it. You will have to use TouchableOpacity inside of your SearchComponent.js.

Your updated App.js should look like this,

import React, { useState } from "react";
import { StyleSheet, TouchableOpacity, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";

import SearchComponent from "./assets/components/SearchComp";
import MapScreen from "./assets/screens/MapScreen";

export default function App() {
  const [expanded, setExpanded] = useState(false);

  if (expanded) {
    console.log("dkfjl");
  }

  return (
    <SafeAreaView style={styles.container}>
      <MapScreen />

      <View style={styles.searchContainer}>
        // Remove TouchableOpacity from here
        <SearchComponent
          expanded={expanded}
          setExpanded={(val) => setExpanded(val)} // Passed the state as props to update it from child component
        />
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  searchContainer: {
    bottom: 0,
    position: "absolute",
    width: "100%",
  },
});

And inside of your SearchComponent.js, you will use your TouchableOpacity and update the value of setExpanded like so,

import React, { useEffect, useState } from 'react';
import { Animated,  TouchableOpacity } from 'react-native';
import { TextInput } from 'react-native-paper';

export default function App({ expanded = false, setExpended }) {
  const [search, setSearch] = useState('');

  const [bottom] = useState(new Animated.Value(0));

  useEffect(() => {
    Animated.timing(bottom, {
      toValue: !expanded ? 80 : 10,
      duration: 150,
      useNativeDriver: false,
    }).start();
  }, [expanded, bottom]);

  return (
    <Animated.View
      style={{
        bottom,
        backgroundColor: 'red',
        borderWidth: 3,
        borderColor: 'red',
      }}>
      <TouchableOpacity onPress={()=>setExpended(!expanded) }>
        <TextInput
          placeholder="Search"
          defaultValue={search}
          onChangeText={(e) => setSearch(e)}
          style={{
            backgroundColor: 'white',
            borderTopLeftRadius: 20,
            borderTopRightRadius: 20,
            borderBottomLeftRadius: 20,
            borderBottomRightRadius: 20,    
          }}
        />
      </TouchableOpacity>
    </Animated.View>
  );
}