@react-navigation/native#Route TypeScript Examples

The following examples show how to use @react-navigation/native#Route. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: CardStack.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private handleHeaderLayout = ({
    route,
    height,
  }: {
    route: Route<string>;
    height: number;
  }) => {
    this.setState(({ headerHeights }) => {
      const previousHeight = headerHeights[route.key];

      if (previousHeight === height) {
        return null;
      }

      return {
        headerHeights: {
          ...headerHeights,
          [route.key]: height,
        },
      };
    });
  };
Example #2
Source File: CardStack.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private getPreviousScene = ({ route }: { route: Route<string> }) => {
    const { getPreviousRoute } = this.props;
    const { scenes } = this.state;

    const previousRoute = getPreviousRoute({ route });

    if (previousRoute) {
      const previousScene = scenes.find(
        (scene) => scene.route.key === previousRoute.key
      );

      return previousScene;
    }

    return undefined;
  };
Example #3
Source File: CardStack.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
getHeaderHeights = (
  routes: Route<string>[],
  insets: EdgeInsets,
  descriptors: StackDescriptorMap,
  layout: Layout,
  previous: Record<string, number>
) => {
  return routes.reduce<Record<string, number>>((acc, curr) => {
    const { options = {} } = descriptors[curr.key] || {};
    const style: any = StyleSheet.flatten(options.headerStyle || {});

    const height =
      typeof style.height === 'number' ? style.height : previous[curr.key];

    const safeAreaInsets = {
      ...insets,
      ...options.safeAreaInsets,
    };

    const { headerStatusBarHeight = safeAreaInsets.top } = options;

    acc[curr.key] =
      typeof height === 'number'
        ? height
        : getDefaultHeaderHeight(layout, headerStatusBarHeight);

    return acc;
  }, {});
}
Example #4
Source File: StackView.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private getGesturesEnabled = ({ route }: { route: Route<string> }) => {
    const descriptor = this.state.descriptors[route.key];

    if (descriptor) {
      const { gestureEnabled, animationEnabled } = descriptor.options;

      if (animationEnabled === false) {
        // When animation is disabled, also disable gestures
        // The gesture to dismiss a route will look weird when not animated
        return false;
      }

      return gestureEnabled !== false;
    }

    return false;
  };
Example #5
Source File: StackView.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private getPreviousRoute = ({ route }: { route: Route<string> }) => {
    const { closingRouteKeys, replacingRouteKeys } = this.state;
    const routes = this.state.routes.filter(
      (r) =>
        r.key === route.key ||
        (!closingRouteKeys.includes(r.key) &&
          !replacingRouteKeys.includes(r.key))
    );

    const index = routes.findIndex((r) => r.key === route.key);

    return routes[index - 1];
  };
Example #6
Source File: StackView.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private renderScene = ({ route }: { route: Route<string> }) => {
    const descriptor =
      this.state.descriptors[route.key] || this.props.descriptors[route.key];

    if (!descriptor) {
      return null;
    }

    return descriptor.render();
  };
Example #7
Source File: StackView.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
    const { state, navigation } = this.props;
    const { closingRouteKeys, replacingRouteKeys } = this.state;

    if (
      closingRouteKeys.some((key) => key === route.key) &&
      replacingRouteKeys.every((key) => key !== route.key) &&
      state.routeNames.includes(route.name) &&
      !state.routes.some((r) => r.key === route.key)
    ) {
      // If route isn't present in current state, but was closing, assume that a close animation was cancelled
      // So we need to add this route back to the state
      navigation.navigate(route);
    } else {
      this.setState((state) => ({
        routes: state.replacingRouteKeys.length
          ? state.routes.filter(
              (r) => !state.replacingRouteKeys.includes(r.key)
            )
          : state.routes,
        openingRouteKeys: state.openingRouteKeys.filter(
          (key) => key !== route.key
        ),
        closingRouteKeys: state.closingRouteKeys.filter(
          (key) => key !== route.key
        ),
        replacingRouteKeys: [],
      }));
    }
  };
Example #8
Source File: StackView.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private handleCloseRoute = ({ route }: { route: Route<string> }) => {
    const { state, navigation } = this.props;

    if (state.routes.some((r) => r.key === route.key)) {
      // If a route exists in state, trigger a pop
      // This will happen in when the route was closed from the card component
      // e.g. When the close animation triggered from a gesture ends
      navigation.dispatch({
        ...StackActions.pop(),
        source: route.key,
        target: state.key,
      });
    } else {
      // We need to clean up any state tracking the route and pop it immediately
      this.setState((state) => ({
        routes: state.routes.filter((r) => r.key !== route.key),
        openingRouteKeys: state.openingRouteKeys.filter(
          (key) => key !== route.key
        ),
        closingRouteKeys: state.closingRouteKeys.filter(
          (key) => key !== route.key
        ),
      }));
    }
  };
Example #9
Source File: StackView.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private handleTransitionStart = (
    { route }: { route: Route<string> },
    closing: boolean
  ) =>
    this.props.navigation.emit({
      type: 'transitionStart',
      data: { closing },
      target: route.key,
    });
Example #10
Source File: StackView.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
private handleTransitionEnd = (
    { route }: { route: Route<string> },
    closing: boolean
  ) =>
    this.props.navigation.emit({
      type: 'transitionEnd',
      data: { closing },
      target: route.key,
    });
Example #11
Source File: PreviousSceneContext.tsx    From nlw2-proffy with MIT License 5 votes vote down vote up
PreviousSceneContext = React.createContext<
  Scene<Route<string>> | undefined
>(undefined)
Example #12
Source File: StackView.tsx    From nlw2-proffy with MIT License 5 votes vote down vote up
private handleGestureCancel = ({ route }: { route: Route<string> }) => {
    this.props.navigation.emit({
      type: 'gestureCancel',
      target: route.key,
    });
  };
Example #13
Source File: StackView.tsx    From nlw2-proffy with MIT License 5 votes vote down vote up
private handleGestureEnd = ({ route }: { route: Route<string> }) => {
    this.props.navigation.emit({
      type: 'gestureEnd',
      target: route.key,
    });
  };
Example #14
Source File: StackView.tsx    From nlw2-proffy with MIT License 5 votes vote down vote up
private handleGestureStart = ({ route }: { route: Route<string> }) => {
    this.props.navigation.emit({
      type: 'gestureStart',
      target: route.key,
    });
  };
Example #15
Source File: StackView.tsx    From nlw2-proffy with MIT License 4 votes vote down vote up
static getDerivedStateFromProps(
    props: Readonly<Props>,
    state: Readonly<State>
  ) {
    // If there was no change in routes, we don't need to compute anything
    if (
      (props.state.routes === state.previousRoutes ||
        isArrayEqual(
          props.state.routes.map((r) => r.key),
          state.previousRoutes.map((r) => r.key)
        )) &&
      state.routes.length
    ) {
      let routes = state.routes;
      let previousRoutes = state.previousRoutes;
      let descriptors = props.descriptors;
      let previousDescriptors = state.previousDescriptors;

      if (props.descriptors !== state.previousDescriptors) {
        descriptors = state.routes.reduce<StackDescriptorMap>((acc, route) => {
          acc[route.key] =
            props.descriptors[route.key] || state.descriptors[route.key];

          return acc;
        }, {});

        previousDescriptors = props.descriptors;
      }

      if (props.state.routes !== state.previousRoutes) {
        // if any route objects have changed, we should update them
        const map = props.state.routes.reduce<Record<string, Route<string>>>(
          (acc, route) => {
            acc[route.key] = route;
            return acc;
          },
          {}
        );

        routes = state.routes.map((route) => map[route.key] || route);
        previousRoutes = props.state.routes;
      }

      return {
        routes,
        previousRoutes,
        descriptors,
        previousDescriptors,
      };
    }

    // Here we determine which routes were added or removed to animate them
    // We keep a copy of the route being removed in local state to be able to animate it

    let routes =
      props.state.index < props.state.routes.length - 1
        ? // Remove any extra routes from the state
          // The last visible route should be the focused route, i.e. at current index
          props.state.routes.slice(0, props.state.index + 1)
        : props.state.routes;

    // Now we need to determine which routes were added and removed
    let {
      openingRouteKeys,
      closingRouteKeys,
      replacingRouteKeys,
      previousRoutes,
    } = state;

    const previousFocusedRoute = previousRoutes[previousRoutes.length - 1] as
      | Route<string>
      | undefined;
    const nextFocusedRoute = routes[routes.length - 1];

    const isAnimationEnabled = (key: string) => {
      const descriptor = props.descriptors[key] || state.descriptors[key];

      return descriptor ? descriptor.options.animationEnabled !== false : true;
    };

    const getAnimationTypeForReplace = (key: string) => {
      const descriptor = props.descriptors[key] || state.descriptors[key];

      return descriptor.options.animationTypeForReplace ?? 'push';
    };

    if (
      previousFocusedRoute &&
      previousFocusedRoute.key !== nextFocusedRoute.key
    ) {
      // We only need to animate routes if the focused route changed
      // Animating previous routes won't be visible coz the focused route is on top of everything

      if (!previousRoutes.some((r) => r.key === nextFocusedRoute.key)) {
        // A new route has come to the focus, we treat this as a push
        // A replace can also trigger this, the animation should look like push

        if (
          isAnimationEnabled(nextFocusedRoute.key) &&
          !openingRouteKeys.includes(nextFocusedRoute.key)
        ) {
          // In this case, we need to animate pushing the focused route
          // We don't care about animating any other added routes because they won't be visible
          openingRouteKeys = [...openingRouteKeys, nextFocusedRoute.key];

          closingRouteKeys = closingRouteKeys.filter(
            (key) => key !== nextFocusedRoute.key
          );
          replacingRouteKeys = replacingRouteKeys.filter(
            (key) => key !== nextFocusedRoute.key
          );

          if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
            // The previous focused route isn't present in state, we treat this as a replace

            openingRouteKeys = openingRouteKeys.filter(
              (key) => key !== previousFocusedRoute.key
            );

            if (getAnimationTypeForReplace(nextFocusedRoute.key) === 'pop') {
              closingRouteKeys = [
                ...closingRouteKeys,
                previousFocusedRoute.key,
              ];

              // By default, new routes have a push animation, so we add it to `openingRouteKeys` before
              // But since user configured it to animate the old screen like a pop, we need to add this without animation
              // So remove it from `openingRouteKeys` which will remove the animation
              openingRouteKeys = openingRouteKeys.filter(
                (key) => key !== nextFocusedRoute.key
              );

              // Keep the route being removed at the end to animate it out
              routes = [...routes, previousFocusedRoute];
            } else {
              replacingRouteKeys = [
                ...replacingRouteKeys,
                previousFocusedRoute.key,
              ];

              closingRouteKeys = closingRouteKeys.filter(
                (key) => key !== previousFocusedRoute.key
              );

              // Keep the old route in the state because it's visible under the new route, and removing it will feel abrupt
              // We need to insert it just before the focused one (the route being pushed)
              // After the push animation is completed, routes being replaced will be removed completely
              routes = routes.slice();
              routes.splice(routes.length - 1, 0, previousFocusedRoute);
            }
          }
        }
      } else if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
        // The previously focused route was removed, we treat this as a pop

        if (
          isAnimationEnabled(previousFocusedRoute.key) &&
          !closingRouteKeys.includes(previousFocusedRoute.key)
        ) {
          closingRouteKeys = [...closingRouteKeys, previousFocusedRoute.key];

          // Sometimes a route can be closed before the opening animation finishes
          // So we also need to remove it from the opening list
          openingRouteKeys = openingRouteKeys.filter(
            (key) => key !== previousFocusedRoute.key
          );
          replacingRouteKeys = replacingRouteKeys.filter(
            (key) => key !== previousFocusedRoute.key
          );

          // Keep a copy of route being removed in the state to be able to animate it
          routes = [...routes, previousFocusedRoute];
        }
      } else {
        // Looks like some routes were re-arranged and no focused routes were added/removed
        // i.e. the currently focused route already existed and the previously focused route still exists
        // We don't know how to animate this
      }
    } else if (replacingRouteKeys.length || closingRouteKeys.length) {
      // Keep the routes we are closing or replacing if animation is enabled for them
      routes = routes.slice();
      routes.splice(
        routes.length - 1,
        0,
        ...state.routes.filter(({ key }) =>
          isAnimationEnabled(key)
            ? replacingRouteKeys.includes(key) || closingRouteKeys.includes(key)
            : false
        )
      );
    }

    if (!routes.length) {
      throw new Error(
        'There should always be at least one route in the navigation state.'
      );
    }

    const descriptors = routes.reduce<StackDescriptorMap>((acc, route) => {
      acc[route.key] =
        props.descriptors[route.key] || state.descriptors[route.key];

      return acc;
    }, {});

    return {
      routes,
      previousRoutes: props.state.routes,
      previousDescriptors: props.descriptors,
      openingRouteKeys,
      closingRouteKeys,
      replacingRouteKeys,
      descriptors,
    };
  }
Example #16
Source File: AnimatedTabBar.tsx    From curved-bottom-navigation-bar with MIT License 4 votes vote down vote up
AnimatedTabBarComponent = (props: AnimatedTabBarProps) => {
  // props
  const {
    navigation,
    tabs,
    descriptors,
    state,
    duration = DEFAULT_ITEM_ANIMATION_DURATION,
    barColor = TAB_BAR_COLOR,
    dotSize = SIZE_DOT,
    barHeight = TAB_BAR_HEIGHT,
    dotColor = TAB_BAR_COLOR,
    titleShown = false,
    barWidth,
  } = props;

  // variables

  const {
    routes,
    index: navigationIndex,
    key: navigationKey,
  } = useMemo(() => {
    return state;
  }, [state]);

  // reanimated
  const selectedIndex = useSharedValue(0);

  // callbacks
  const getRouteTitle = useCallback(
    (route: Route<string>) => {
      const { options } = descriptors[route.key];
      // eslint-disable-next-line no-nested-ternary
      return options.tabBarLabel !== undefined &&
        typeof options.tabBarLabel === 'string'
        ? options.tabBarLabel
        : options.title !== undefined
        ? options.title
        : route.name;
    },
    [descriptors]
  );

  const getRouteTabConfigs = useCallback(
    (route: Route<string>) => {
      return tabs[route.name];
    },
    [tabs]
  );

  const getRoutes = useCallback(() => {
    return routes.map((route) => ({
      key: route.key,
      title: getRouteTitle(route),
      ...getRouteTabConfigs(route),
    }));
  }, [routes, getRouteTitle, getRouteTabConfigs]);

  const handleSelectedIndexChange = useCallback(
    (index: number) => {
      const { key, name } = routes[index];
      const event = navigation.emit({
        type: 'tabPress',
        target: key,
        canPreventDefault: true,
      });

      if (!event.defaultPrevented) {
        navigation.dispatch({
          ...CommonActions.navigate(name),
          target: navigationKey,
        });
      }
    },
    [routes, navigation, navigationKey]
  );

  // effects
  useEffect(() => {
    selectedIndex.value = navigationIndex;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [navigationIndex]);

  useAnimatedReaction(
    () => selectedIndex.value,
    (nextSelected, prevSelected) => {
      if (nextSelected !== prevSelected) {
        runOnJS(handleSelectedIndexChange)(nextSelected);
      }
    },
    [selectedIndex, handleSelectedIndexChange]
  );

  // render
  return (
    <CurvedTabBar
      isRtl={I18nManager.isRTL}
      barWidth={barWidth}
      titleShown={titleShown}
      dotColor={dotColor}
      barHeight={barHeight}
      dotSize={dotSize}
      tabBarColor={barColor}
      selectedIndex={selectedIndex}
      navigationIndex={navigationIndex}
      routes={getRoutes()}
      duration={duration}
    />
  );
}
Example #17
Source File: StackNavigator.tsx    From sellflow with MIT License 4 votes vote down vote up
export default function StackNavigator() {
  let { authToken } = useAuth();
  let { data: userData } = useGetAuthenticatedUser();
  let { isRTL } = useTheme();

  function getTabSceneName(route: Pick<Route<string>, 'key' | 'name'>) {
    const routeName = getFocusedRouteNameFromRoute(route) || 'HomeTab';
    return routeName;
  }

  return (
    <Stack.Navigator
      screenOptions={headerOptions}
      headerMode="screen"
      initialRouteName={'Home'}
    >
      <Stack.Screen
        name="Home"
        component={TabNavigator}
        options={({ navigation, route }) => {
          let tabScene = getTabSceneName(route);
          if (tabScene === 'HomeTab') {
            return {
              title:
                authToken && userData?.authenticatedUser.firstName
                  ? `${t('Hello')}, ${userData.authenticatedUser.firstName}`
                  : t('Hello'),
              headerLeft: () => <LocalizationPicker />,
              headerRight: () => (
                <HeaderIconButton
                  icon="cart"
                  onPress={() => navigation.navigate('ShoppingCart')}
                />
              ),
              headerStyle: {
                shadowColor: COLORS.transparent,
                elevation: 0,
              },
            };
          } else if (tabScene === 'WishlistTab') {
            return {
              headerLeft: () => null,
              title: t('Wishlist'),
            };
          } else {
            return authToken
              ? {
                  headerLeft: () => null,
                  title: t('My Profile'),
                }
              : {
                  headerLeft: () =>
                    !authToken && (
                      <HeaderIconButton
                        icon={isRTL ? 'chevron-right' : 'chevron-left'}
                        onPress={() => navigation.navigate('HomeTab')}
                      />
                    ),
                  title: '',
                  headerStyle: {
                    shadowColor: COLORS.transparent,
                    elevation: 0,
                  },
                };
          }
        }}
      />
      <Stack.Screen
        name="Auth"
        component={AuthScene}
        options={() => ({
          title: t('Welcome'),
          headerStyle: {
            shadowColor: COLORS.transparent,
            elevation: 0,
          },
        })}
      />
      <Stack.Screen
        name="ForgotPassword"
        component={ForgotPasswordScene}
        options={() => ({
          title: t('Forgot Password'),
          cardStyle: {
            backgroundColor: COLORS.white,
          },
        })}
      />
      <Stack.Screen
        name="AddressManagement"
        component={AddressManagementScene}
        options={() => ({
          title: t('Manage Addresses'),
        })}
      />
      <Stack.Screen name="AddEditAddress" component={AddEditAddressScene} />
      <Stack.Screen
        name="EditProfile"
        component={EditProfileScene}
        options={() => ({
          title: t('Edit Profile'),
        })}
      />
      <Stack.Screen
        name="OrderHistory"
        component={OrderHistoryScene}
        options={() => ({
          title: t('Order History'),
          cardStyle: {
            backgroundColor: COLORS.darkWhite,
          },
        })}
      />
      <Stack.Screen
        name="OrderDetails"
        component={OrderDetailsScene}
        options={() => ({
          title: t('Order Details'),
        })}
      />
      <Stack.Screen
        name="ProductDetails"
        component={ProductDetailsScene}
        options={({ navigation }) => ({
          title: t('Product Details'),
          headerRight: () => (
            <HeaderIconButton
              icon="cart"
              onPress={() => navigation.navigate('ShoppingCart')}
            />
          ),
        })}
      />
      <Stack.Screen
        name="ShoppingCart"
        component={ShoppingCartScene}
        options={() => ({
          title: t('Shopping Cart'),
        })}
      />
      <Stack.Screen
        name="ProductCollection"
        component={ProductCollectionScene}
        options={({ route }) => ({
          title: route.params.collection.title,
        })}
      />
      <Stack.Screen
        name="SearchResults"
        component={SearchResultsScene}
        options={() => ({
          title: t('Search Results'),
        })}
      />
      <Stack.Screen
        name="Checkout"
        component={CheckoutScene}
        options={() => ({
          title: t('Checkout'),
        })}
      />
      <Stack.Screen name="WebView" component={WebViewScene} />

      <Stack.Screen
        name="OrderPlacedConfirmation"
        component={OrderPlacedConfirmationScene}
        options={() => ({
          title: t('Order Placed'),
          headerLeft: () => null,
        })}
      />
    </Stack.Navigator>
  );
}