reactnavigation.orgCustom navigators 번역 자료입니다. 번역에 문제가 있다면 댓글 달아주시구요. 원문을 보시기를 추천드립니다

네비게이터(Navigators)를 사용하면 응용 프로그램의 네비게이션 구조(structure)를 정의 할 수 있습니다. 또한 네비게이터는 구성 할 수있는 헤더, 탭바 같은 공통 요소(common elements)를 렌더링합니다.

후드 아래에서(Under the hood) 네비게이터는 일반 React 컴포넌트입니다.

내장 네비게이터(Built-in Navigators)

다음과 같이 일반적으로 필요한 네비게이터를 포함합니다. :

  • createStackNavigator - 한 번에 하나의 스크린을 렌더링하고 스크린 간 전환을 제공합니다. 새 스크린이 열리면 스택 위에 배치됩니다.
  • createDrawerNavigator - 기본적으로 스크린 왼쪽에서 슬라이드하는 드로어(drawer)를 제공합니다.
  • createBottomTabNavigator - 사용자가 여러 스크린을 전환 할 수있는 탭바를 렌더링합니다.
  • createMaterialTopTabNavigator - 스와이프 제스처 또는 탭바를 사용하여 여러 스크린간에 전환 할 수 있는 탭뷰를 렌더링합니다.
  • createMaterialBottomTabNavigator - 스와이프 제스처 또는 탭바를 사용하여 여러 스크린 간을 전환 할 수 있는 탭뷰를 렌더링합니다.

커스텀 네비게이터 빌딩을 위한 API

네비게이터는 라우터와 네비게이션 state를 가져 와서 렌더링 방법을 결정하는 뷰를 번들로 제공합니다. 우리는 useNavigationBuilder hook을 내보내 나머지 React Navigation과 통합되는 커스텀 네비게이터를 빌드합니다.

useNavigationBuilder

이 hook을 사용하면 컴포넌트를 React Navigation에 연결할 수 있습니다. 다음과 같은 인수를 허용합니다.

  • createRouter - 라우터 오브젝트를 반환하는 팩토리 메소드 (e.g. StackRouter, TabRouter).

  • options - hook 및 라우터 옵션. 네비게이터는 사용자가 네비게이터를 구성하기 위한 prop을 제공 할 수 있도록 여기에 prop을 전달해야 합니다. 기본적으로 다음 옵션이 허용됩니다.

    • children(required) - childrenprop에는 Screen컴포넌트로 route configurations을 포함해야 합니다 .
    • screenOptions - screenOptions prop에는 모든 스크린에 대한 default option이 포함되어 있어야 합니다.
    • initialRouteName - initialRouteNameprop은 초기 렌더링(initial render)에 초점을 맞춰 스크린을 결정합니다. 이 prop은 라우터로 전달됩니다.

    다른 옵션이 여기에 전달되면 라우터로 전달됩니다.

hook은 다음 특성을 가진 오브젝트를 리턴합니다.

  • state - 네비게이터의 네비게이션 state. 컴포넌트는 이 state를 가져와서 렌더링 방법을 결정할 수 있습니다.
  • navigation - 네비게이터가 네비게이션 state를 조작 할 수 있도록 다양한 헬퍼 메소드가 포함된 네비게이션 오브젝트. 이것은 스크린의 네비게이션 오브젝트와 같지 않으며 스크린에 emit이벤트를 발산(emit) 하는 것과 같은 일부 헬퍼를 포함 합니다.
  • descriptors - 이것은 route keys를 속성으로 하여 각 경로에 대한 descriptor를 포함하는 오브젝트입니다. 경로에 대한 descriptor는 descriptors[route.key]에 의해 액세스할 수 있습니다. 각 descriptor는 다음 속성을 포함합니다.
    • navigation - 스크린의 네비게이션 도구. 스크린에 수동으로 전달할 필요는 없습니다. 그러나 navigation 헤더 컴포넌트와 같이 prop을 수신 해야하는 컴포넌트를 스크린 외부(outside)에 렌더링하는 경우 유용합니다 .
    • options - 지정된 경우 스크린 title과 같은 옵션을 반환하는 getter.
    • render - 실제 스크린을 렌더링하는 데 사용할 수있는 기능입니다. descriptors[route.key].render() 를 호출 하면 스크린 내용이 포함 된 React 요소가 반환됩니다. 이 방법을 사용하여 스크린을 렌더링하는 것이 중요합니다. 그렇지 않으면 하위 네비게이터가 네비게이션 트리에 제대로 연결되지 않습니다.

예제:

import * as React from 'react';
import {
  useNavigationBuilder,
  TabRouter,
  TabActions,
} from '@react-navigation/native';

function TabNavigator({
  initialRouteName,
  children,
  screenOptions,
  tabBarStyle,
  contentStyle,
}) {
  const { state, navigation, descriptors } = useNavigationBuilder(TabRouter, {
    children,
    screenOptions,
    initialRouteName,
  });

  return (
    <React.Fragment>
      <View style={[{ flexDirection: 'row' }, tabBarStyle]}>
        {state.routes.map(route => (
          <TouchableOpacity
            key={route.key}
            onPress={() => {
              const event = navigation.emit({
                type: 'tabPress',
                target: route.key,
                canPreventDefault: true,
              });

              if (!event.defaultPrevented) {
                navigation.dispatch({
                  ...TabActions.jumpTo(route.name),
                  target: state.key,
                });
              }
            }}
            style={{ flex: 1 }}
          >
            <Text>{descriptors[route.key].options.title || route.name}</Text>
          </TouchableOpacity>
        ))}
      </View>
      <View style={[{ flex: 1 }, contentStyle]}>
        {descriptors[state.routes[state.index].key].render()}
      </View>
    </React.Fragment>
  );
}

네비게이터를 위한 navigation오브젝트에는 child 스크린에 커스텀 이벤트를 내보내는 emit 메소드도 있습니다. 사용법은 다음과 같습니다.

navigation.emit({
  type: 'transitionStart',
  data: { blurring: false },
  target: route.key,
});

data 이벤트 오브젝트의 data 속성 (i.e. : event.data)에서 사용할 수 있습니다.

target 프로퍼티는 이벤트를 받을 스크린을 결정합니다. target속성을 생략하면 이벤트가 네비게이터의 모든 스크린으로 전달됩니다.

createNavigatorFactory

createNavigatorFactory 함수는 네비게이터와 스크린의 쌍(pair)을 만드는 함수를 작성하는데 사용됩니다. 커스텀 네비게이터는 내보내기 전에 네비게이터 구성 요소를 createNavigatorFactory로 감싸야(wrap)합니다.

예:

import {
  useNavigationBuilder,
  createNavigatorFactory,
} from '@react-navigation/native';

// ...

export default createNavigatorFactory(TabNavigator);

Type-checking 네비게이터

네비게이터의 타입을 체크 하려면 3가지 타입을 제공해야 합니다.

  • 뷰에서 승인 한 prop의 타입
  • 지원되는 뷰 옵션 타입
  • 네비게이터가 생성 한 이벤트 타입의 맵

예를 들어 커스텀 탭 네비게이터의 타입을 체크하려면 다음과 같이 할 수 있습니다.

import * as React from 'react';
import { StyleProp, ViewStyle } from 'react-native';
import {
  useNavigationBuilder,
  DefaultNavigatorOptions,
  TabRouter,
  TabActions,
  TabRouterOptions,
  TabNavigationState,
} from '@react-navigation/native';

// Props accepted by the view
type TabNavigationConfig = {
  tabBarStyle: StyleProp<ViewStyle>;
  contentStyle: StyleProp<ViewStyle>;
};

// Supported screen options
type TabNavigationOptions = {
  title?: string;
};

// Map of events and the type of data (in event.data)
type TabNavigationEventMap = {
  tabPress: { isAlreadyFocused: boolean };
};

// The props accepted by the component is a combination of 3 things
type Props = DefaultNavigatorOptions<TabNavigationOptions> &
  TabRouterOptions &
  TabNavigationConfig;

function TabNavigator({
  initialRouteName,
  children,
  screenOptions,
  tabBarStyle,
  contentStyle,
}: Props) {
  const { state, navigation, descriptors } = useNavigationBuilder<
    TabNavigationState,
    TabRouterOptions,
    TabNavigationOptions,
    TabNavigationEventMap
  >(TabRouter, {
    children,
    screenOptions,
    initialRouteName,
  });

  return (
    <React.Fragment>
      <View style={[{ flexDirection: 'row' }, tabBarStyle]}>
        {state.routes.map(route => (
          <TouchableOpacity
            key={route.key}
            onPress={() => {
              const event = navigation.emit({
                type: 'tabPress',
                target: route.key,
                data: {
                  isAlreadyFocused: route.key === state.routes[state.index].key,
                },
              });

              if (!event.defaultPrevented) {
                navigation.dispatch({
                  ...TabActions.jumpTo(route.name),
                  target: state.key,
                });
              }
            }}
            style={{ flex: 1 }}
          >
            <Text>{descriptors[route.key].options.title || route.name}</Text>
          </TouchableOpacity>
        ))}
      </View>
      <View style={[{ flex: 1 }, contentStyle]}>
        {descriptors[state.routes[state.index].key].render()}
      </View>
    </React.Fragment>
  );
}

export default createNavigatorFactory<
  TabNavigationOptions,
  typeof TabNavigator
>(TabNavigator);

네비게이터 확장

모든 내장(built-in) 네비게이터는 뷰를 내보내므로 그 위에 추가 기능을 재사용하고 빌드할 수 있습니다. 예를 들면 하단 탭 네비게이터를 다시 빌드하려면 다음 코드가 필요합니다.

import * as React from 'react';
import {
  useNavigationBuilder,
  createNavigatorFactory,
  TabRouter,
} from '@react-navigation/native';
import { BottomTabView } from '@react-navigation/bottom-tabs';

function BottomTabNavigator({
  initialRouteName,
  backBehavior,
  children,
  screenOptions,
  ...rest
}) {
  const { state, descriptors, navigation } = useNavigationBuilder(TabRouter, {
    initialRouteName,
    backBehavior,
    children,
    screenOptions,
  });

  return (
    <BottomTabView
      {...rest}
      state={state}
      navigation={navigation}
      descriptors={descriptors}
    />
  );
}

export default createNavigatorFactory(BottomTabNavigator);

이제 추가 기능을 추가하거나 동작을 변경하도록 커스터마이즈 할 수 있습니다. 예를 들면 기본 TabRouter 대신 커스텀 라우터를 사용하세요.

import MyRouter from './MyRouter';

// ...

const { state, descriptors, navigation } = useNavigationBuilder(MyRouter, {
  initialRouteName,
  backBehavior,
  children,
  screenOptions,
});

// ...