React Native клик не на меню

Начал изучать React Native и появился вопрос. Делаю боковое меню три точки. Пробовал react-native-popup-menu, но оно тоже не закрывается, если кликать не в него.

import React, { useState } from 'react';
import { FlatList, View,} from 'react-native';
import { Title } from './Title';


export const MainScreen = () => {
  const [mainTitle, setMainTitle] = useState([
  {id: 1, title: 'test1' },
  { id: 2, title: 'test2' }
]);

  return( 
    <View style={{flex:1}}>

      <FlatList
        keyExtractor={item => item.id.toString()}
        data={mainTitle}
        onEndReachedThreshold={0.7}
        initialNumToRender={7}
        renderItem={({item})=> 
       <Title title={item.title}/>
        }
      />
    </View>
  );
};

и код Title

import React, { useState } from 'react';
import { StyleSheet, View, TouchableOpacity, Text, } from 'react-native';
import {  MaterialCommunityIcons  } from '@expo/vector-icons';
import {THEME} from '../theme';


export const Title = ({title}) => {
    
  const [target, setTarget] = useState(false);
 
  let menu;

  if(target){
    menu = (
      <View style={styles.menu} >
        <TouchableOpacity
          style = {[styles.btn]}
          onPress = {() => {  console.log('Сохранить') }} >
       <Text>Сохранить</Text>
      </TouchableOpacity>

      <TouchableOpacity
      style = {styles.btn}
      onPress = {() => { console.log('Удалить') }}
      >
        <Text>Удалить</Text>
      </TouchableOpacity>
    </View>
  )
  }else{
    menu=null
  }


  return( 
    <>
      <Text>{title}</Text>
      <TouchableOpacity
        style = {styles.button}
        onPress = {() => {
          setTarget(target? false : true)
          }
        }
      >
        <MaterialCommunityIcons name="dots-vertical" size={24} color={THEME.BLACK_COLOR}  />
      </TouchableOpacity>
      {menu}
    </>
  );
};

const styles = StyleSheet.create({
  menu:{
    alignItems: 'center',
    borderWidth: 2,
    borderColor: THEME.COLOR,
  },
    btn:{
    alignItems: 'center',
    margin: 10,
    height:'10%',
    width: '20%',
  },
    button:{
    width: '8%',
    height: '75%',
    height: 40,
    width: 40,
    marginTop:10,
  }
});

когда открыто {menu} если клик будет не на {menu}, то {menu} должно закрываться. Не могу понять как клик не на {menu} поймать .


Ответы (1 шт):

Автор решения: MrYogurt

Как связать дочерний компонент с родителем:

const Parent = () => {

  const handleClick = (event) => {
    console.log("Clicked", event)
  }

return (
  <>
    <Child handleClick={handleClick} /> // передаем обработчик дочернему компоненту
  </>
)
}

const Child = (handleClick) => { // принимаем пропсом функцию в дочернем компоненте

return <Button onClick={(event) => handleClick(event)} /> // вызываем родительскую функцию и передаем ей ивент
}

По вашему коду все равно непонятно, как должно работать это меню, нужно заливать приложение на codepen и кидать ссылку. Но я на основе вашего кода описал, как вообще его можно создать:

import React, { useState } from 'react';
import { StyleSheet, View, TouchableOpacity, Text, } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { THEME } from '../theme';

import { FlatList, } from 'react-native';
import { Title } from './Title';


export const MainScreen = () => {
  const [mainTitle, setMainTitle] = useState([
    { id: 1, title: 'test1' },
    { id: 2, title: 'test2' }
  ]);

  return (
    <View style={{ flex: 1 }}>

      <FlatList
        keyExtractor={item => item.id.toString()}
        data={mainTitle}
        onEndReachedThreshold={0.7}
        initialNumToRender={7}
        renderItem={({ item }) =>
          <Title title={item.title} />
        }
      />
    </View>
  );
};


export const Title = ({ title }) => {

  const [target, setTarget] = useState(false);

  return (
    <>
      <Text>{title}</Text>
      <TouchableOpacity
        style={styles.button}
        onPress={() => setTarget(target ? false : true)}
      >
        <MaterialCommunityIcons name="dots-vertical" size={24} color={THEME.BLACK_COLOR} />
      </TouchableOpacity>

      {target && ( // заменил изначальный бред с записью в переменную, теперь меню будет отрисовано, когда таргет тру, так же можно использовать тернарное выражение
        <View style={{ position: "absolute", width: "100%", height: "100%" }}> // делаем общий контейнер для меню и бэкграунда на весь экран в абсолюте, чтобы наложить поверх прочих элементов
          <View style={styles.menu}> // здесь нужно прописать размерные стили для самого меню
            <TouchableOpacity
              style={[styles.btn]}
              onPress={() => console.log('Сохранить')} >
              <Text>Сохранить</Text>
            </TouchableOpacity>

            <TouchableOpacity
              style={styles.btn}
              onPress={() => console.log('Удалить')}
            >
              <Text>Удалить</Text>
            </TouchableOpacity>
          </View>
          <View style={{ width: "100%", height: "100%" }}>
            <TouchableOpacity style={{ width: "100%", height: "100%" }} onClick={() => setTarget(false)} /> // здесь растягиваем контейнер с обработчиком клика на весь экран и если все сделано правильно, контейнер будет находиться позади меню и при нажатии на пустое место за меню, будет происходить изменение стейта таргет на фолс
          </View>
        </View>
      )}
    </>
  );
};

const styles = StyleSheet.create({
  menu: {
    alignItems: 'center',
    borderWidth: 2,
    borderColor: THEME.COLOR,
  },
  btn: {
    alignItems: 'center',
    margin: 10,
    height: '10%',
    width: '20%',
  },
  button: {
    width: '8%',
    height: '75%',
    height: 40,
    width: 40,
    marginTop: 10,
  }
});
→ Ссылка