# flatlist inside scrollview

VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.

# before


<ScrollView >
    <FlatList />
</ScrollView>

# after

<ScrollView horizontal={false} style={{width: '100%', height: '100%'}}>
    <ScrollView horizontal={true} style={{width: '100%', height: '100%'}}>
        <FlatList />
    </ScrollView>
</ScrollView>

# Drawer

function MainStackComponent() {
  const MainStack = createStackNavigator();
  return (
    <MainStack.Navigator>
      <MainStack.Screen name="HomeIndex" component={HomeIndex} options={({route}) => ({title: route.name, headerShown: false})} />
    </MainStack.Navigator>
  );
}

function YoutubeIframeStackComponent() {
  // var videoId = route?.params?.id;
  const StackNavigator = createStackNavigator();
  return (
    <StackNavigator.Navigator>
      <StackNavigator.Screen name="YoutubeIframeIndex" component={YoutubeIframeIndex} options={({route}) => ({title: route.name, headerShown: false})} />
    </StackNavigator.Navigator>
  );
}

// λ“œλ‘œμ–΄ λ„€λΉ„κ²Œμ΄ν„°
const DrawerNav = ({navigation, route}) => {
  const DrawerNavigator = createDrawerNavigator();
  return (
    <DrawerNavigator.Navigator>
      <DrawerNavigator.Screen
        key={'HomeIndex'}
        name={'home'}
        component={MainStackComponent}
        options={{
          headerShown: false,
          unmountOnBlur: true,
        }}
      />
      <DrawerNavigator.Screen
        key={'YoutubeIframeIndex'}
        name={'youtube'}
        component={YoutubeIframeStackComponent}
        options={{
          headerShown: false,
          unmountOnBlur: true,
        }}
      />
    </DrawerNavigator.Navigator>
  );
};

# BottomTab

const MainTabNav = ({navigation, route}) => {
  const BottomTabNavigator = createBottomTabNavigator();
  // var videoId = route?.params?.id;
  return (
    <BottomTabNavigator.Navigator>
      {bottomRoutes.map(data => (
        <BottomTabNavigator.Screen
          // initialParams={{id: videoId}}
          key={data.name}
          name={data.label}
          component={data.component}
          options={{
            tabBarShowLabel: false,
            headerShown: false,
            unmountOnBlur: true,
            tabBarIcon: ({focused}) => {
              return <Image source={focused ? data.activeMenu : route.inactiveMenu} style={{width: 15, height: 15}} />;
            },
            tabBarStyle: {height: 50},
          }}
        />
      ))}
    </BottomTabNavigator.Navigator>
  );
};

const bottomRoutes = [
  {
    name: 'home',
    label: 'home',
    component: HomeIndex,
    inactiveMenu: Images.menu_home_off,
    activeMenu: Images.menu_home_on,
  },
  {
    name: 'iframe',
    label: 'iframe',
    component: YoutubeIframeIndex,
    inactiveMenu: Images.menu_youtube_off,
    activeMenu: Images.menu_youtube_on,
  },
];

# we have failed to run 'bundletool build-apks' on this android app bundle

# The emulator process for AVD Pixel5API31 has terminated.

https://codingjerk-diary.tistory.com/entry/Android%EC%98%A4%EB%A5%98%ED%95%B4%EA%B2%B0-The-emulator-process-for-AVD-has-terminated

# custom react-native-appstate-hook

// https://github.com/amrlabib/react-native-appstate-hook/issues/17
const useAppState = settings => {
  const {onChange, onForeground, onBackground} = settings || {};
  const [appState, setAppState] = useState(AppState.currentState);

  useEffect(() => {
    const handleAppStateChange = nextAppState => {
      if (nextAppState === 'active' && appState !== 'active') {
        isValidFunction(onForeground) && onForeground();
      } else if (
        appState === 'active' &&
        nextAppState.match(/inactive|background/)
      ) {
        isValidFunction(onBackground) && onBackground();
      }
      setAppState(nextAppState);
      isValidFunction(onChange) && onChange(nextAppState);
    };
    AppState.addEventListener('change', handleAppStateChange);

    return () => {
      AppState.removeEventListener('change', handleAppStateChange);
    };
  }, [onChange, onForeground, onBackground, appState]);

  return {appState};
};

# gesture

import {useFocusEffect} from '@react-navigation/native';

  const pan = useRef(new Animated.ValueXY()).current;

  const panResponder = useRef(
    PanResponder.create({
      // onStartShouldSetPanResponder: μŠ€μ™€μ΄ν”„λ₯Ό μ‹œμž‘ν•΄μ•Ό ν• μ§€ μ—¬λΆ€λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.
      onStartShouldSetPanResponder: (evt, gestureState) => true, // μŠ€μ™€μ΄ν”„ ν—ˆμš©

      // onStartShouldSetPanResponderCapture: μŠ€μ™€μ΄ν”„λ₯Ό μœ„ν•œ 캑처 μ—¬λΆ€λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true, // μŠ€μ™€μ΄ν”„ ν—ˆμš©

      // onMoveShouldSetPanResponder: μŠ€μ™€μ΄ν”„λ₯Ό μ§„ν–‰ν• μ§€ μ—¬λΆ€λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.
      onMoveShouldSetPanResponder: (evt, gestureState) => true, // μŠ€μ™€μ΄ν”„ ν—ˆμš©

      // onMoveShouldSetPanResponderCapture: μŠ€μ™€μ΄ν”„λ₯Ό μœ„ν•œ 캑처 μ—¬λΆ€λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, // μŠ€μ™€μ΄ν”„ ν—ˆμš©

      // onPanResponderMove: μŠ€μ™€μ΄ν”„ λ™μž‘μ— λŒ€ν•œ 처리λ₯Ό ν•©λ‹ˆλ‹€.
      onPanResponderMove: (evt, gestureState) => {
        // μŠ€μ™€μ΄ν”„ λ™μž‘μ— λ”°λ₯Έ 처리 둜직

        // console.log('start', evt);
        console.log('start', gestureState);
      },

      // onPanResponderRelease: μŠ€μ™€μ΄ν”„κ°€ 끝났을 λ•Œ 처리λ₯Ό ν•©λ‹ˆλ‹€.
      onPanResponderRelease: (evt, gestureState) => {
        // μŠ€μ™€μ΄ν”„ μ’…λ£Œ ν›„ 처리 둜직
        // console.log('end', evt);
        console.log('end', gestureState);
      },
    }),
  ).current;

              <Animated.View
                style={{
                  transform: [{translateY: pan.y}],
                }}
                {...panResponder.panHandlers}>
              </Animated.View>
Last Updated: 4/13/2025, 11:14:44 PM