npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

typed-react-navigator

v1.0.10

Published

This is a nested router solution meant as a replacement for react-navigation, with a focus on performance and type correctness.

Downloads

9

Readme

React Typed Navigator

This is a nested router solution meant as a replacement for react-navigation, with a focus on performance and type correctness.

First define your routes in a nested manner (typically in a router.tsx file or the like) to create your Navigator and navigation functions. Next, render your Navigator at the root of your app. Finally, use the navigation constants and functions exported from your route definition (e.g. the exports defined in router.tsx) to interact with the router, navigate, get params, etcetera.

The architecture lends itself to aggressive performance optimization since navigators do not need to pass a navigation prop to their children (unlike most solutions). Because of this, we can enable two performance optimzations: (1) navigators are wrapped with React.memo to prevent unnecessary re-renders and (2) We "freeze" not-currently-visible routes to prevent their render functions from firing.

Basic Example:

import { Button, Text, View } from "react-native";
import { createTypedReactNavigator, createRouteDefinition, ParamTypes, lazy } from "typed-react-navigator";
const routeDef = createRouteDefinition({
  //Declare the type of root navigator. Options are "switch" or "stack"
  type: "switch",
  //Declare the routes that live under the root navigator
  routes: {
    //The login route. Is also the initial route since it comes first in the object
    login: {
      //Type "leaf" for the login route which means it does have any nested navigators. Note that most complex apps have nested navigators.
      type: "leaf",
      Component: () => {
        return (
          <View style={{ flex: 1 }}>
            <Text>Login Page</Text>
            <Button
              title="Login"
              onPress={() => {
                //On click, navigate to the main route with { someParam: 123 }
                navigate(PATHS.main, { someParam: 123 });
              }}
            />
          </View>
        );
      },
    },
    //The main route
    main: {
      //The main route is also a leaf route
      type: "leaf",
      params: {
        //Anytime the main route is navigated to there MUST be a someParam parameter defined that is a number
        someParam: ParamTypes.number(),
      },
      Component: () => {
        //Call the useParams method to get the param, and ensure you specify which router path you expect
        //this component to be rendered under. In this case the main route
        const { someParam } = useParams(PATHS.main);
        return (
          <View style={{ flex: 1 }}>
            <Text>Main</Text>
          </View>
        );
      },
    },
  },
});

//Export all the key functions and constants you will use throughout your app. These will be FULLY TYPED using the shape of your route definition
const { Navigator, PATHS, goBack, navigate, useParams } = createTypedReactNavigator(routeDef);

export function App() {
  //Render the navigator at the root of your app
  return <Navigator />;
}

More Complex Example

//router.tsx
import React from "react";
import { Button, Text, View } from "react-native";

import { createTypedReactNavigator, createRouteDefinition, ParamTypes } from "typed-react-navigator";
const routeDef = createRouteDefinition({
  type: "switch",
  routes: {
    login: {
      type: "leaf",
      //Note that
      Component: lazy(() => import("./Login")),
    },
    main: {
      type: "switch",
      //`keepChildrenMounted` makes previously rendered but currently inactive screens stay mounted. The default behavior is to unmount them.
      keepChildrenMounted: true,
      Wrapper: lazy(() => import("./MainLayout")),
      routes: {
        tab_1: {
          type: "stack",
          routes: {
            tab_1_stack_home: {
              type: "leaf",
              Component: lazy(() => import("./Tab1StackHome")),
            },
            tab_1_stack_screen: {
              type: "leaf",
              params: {
                //This parameter can be used by any subroute
                someParam: ParamTypes.number(),
              },
            },
          },
        },
        tab_2: {
          type: "leaf",
          Component: lazy(() => import("./Tab2")),
        },
      },
    },
  },
});

//NOTE EXPORTS BELOW! These are how params will be consumed and navigation will occur.
export const { Navigator, PATHS, goBack, navigate, useParams } = createTypedReactNavigator(routeDef);

//App.tsx
import { Navigator } from "./router.tsx";
export function App() {
  return <Navigator />;
}

//Login.tsx
import { PATHS, navigate } from "./router.tsx";

export default function Login() {
  return (
    <View style={{ flex: 1 }}>
      <Text>This is the Login Page</Text>
      <Button
        onPress={() => {
          navigate(PATHS.main.tab_1.tab_1_stack_home, {});
        }}
        title="Click to Login"
      />
    </View>
  );
}

//MainTabBar.tsx
import { View, Button, Text } from "react-native";
import { PATHS, navigate } from "../Router.js";
export default function MainTabBar() {
  return (
    <View style={{ height: 50, flex: 1 }}>
      <Button
        title="Tab 1"
        onPress={() => {
          navigate(PATHS.main.tab_1.tab_1_stack_home, {});
        }}
      />
      <Button
        title="Tab 2"
        onPress={() => {
          navigate(PATHS.main.tab_2, {});
        }}
      />
    </View>
  );
}

//Tab1StackHome.tsx
import { View, Button, Text } from "react-native";
import { PATHS, navigate } from "../Router.js";
export default function Tab1StackHome() {
  return (
    <View style={{ flex: 1 }}>
      <Text>Tab 1</Text>
      <Button
        onPress={() => {
          navigate(PATHS.main.tab_1.tab_1_stack_screen, { someParam: 123 });
        }}
        title="Push Stack Screen"
      />
    </View>
  );
}

//Tab1StackScreen.tsx
import { goBack, useParams } from "./router.tsx";
export default function Tab1StackScreen() {
  const { someParam } = useParams(PATHS.main.tab_1.tab_1_stack_screen);
  return (
    <View style={{ flex: 1 }}>
      <Text>Tab 1 Stack Screen ({someParam})</Text>
      <Button
        onPress={() => {
          goBack();
        }}
        title="Go Back"
      />
    </View>
  );
}

//Tab2.tsx
export default function Tab2() {
  return (
    <View style={{ flex: 1 }}>
      <Text>Tab 2</Text>
    </View>
  );
}