🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

@karrotframe/navigator-plugin

Package Overview
Dependencies
Maintainers
2
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@karrotframe/navigator-plugin

extension for @karrotframe/navigator

0.6.1
latest
Source
npm
Version published
Weekly downloads
0
Maintainers
2
Weekly downloads
 
Created
Source

@karrotframe/navigator-plugin

한국어

extensible plugin for @karrotframe/navigator

  • 🧩 lifecycle hooks to control event for @karrotframe/navigator
  • 📭 apply middleware to lifecycle hooks
  • 🖇️ independent state manage of plugin to support various scenarios

Install

$ yarn add @karrotframe/navigator-plugin

Create simple plugin

Define plugin

plugins/index.ts

import type {
  PluginType,
  NavigatorPluginType,
} from '@karrotframe/navigator-plugin'

const pluginName = 'SimplePlugin'

export const useSimplePlugin = (): PluginType & {
  pluginName: string
} => {
  return pluginName
}

export const simplePlugin: NavigatorPluginType = {
  name: pluginName,
  executor: useSimplePlugin,
}

Apply simple plugin

App.tsx

import { simplePlugin } from './plugins'

const App: React.FC = () => {
  return (
    <Navigator plugins={[simplePlugin]}>
      <Screen path="/" component={Main} />
    </Navigator>
  )
}

Main.tsx

import { useSimplePlugin } from './plugins'

const Main: React.FC = () => {
  const { pluginName } = useSimplePlugin()
  return (
    <div>
      <span>Main</span>
      <span>{pluginName}</span>
    </div>
  )
}

Create plugin with lifecycle hook of Navigator

Define plugin

plugins/index.ts

import type {
  NavigatorPluginType,
  PluginType,
} from '@karrotframe/navigator-plugin'

export const loggerPlugin: NavigatorPluginType = {
  name: 'loggerPlugin',
  executor: (): PluginType => ({
    lifeCycleHooks: {
      onPoppedWithData: async ({ from, data }) => {
        console.log('from: ', from)
        console.log('data: ', data)
      },
    },
  }),
}

onPoppedWithData hook is called when user calls pop().send(data).

This hook takes from and data arguments as you know.

You could also check other lifecycle hooks like onPoppedWithData.

Create plugin to control state from plugin

Define plugin

plugins/index.ts

import React, { createContext, useContext, useState, useMemo } from 'react'
import type {
  NavigatorPluginType,
  PluginType,
} from '@karrotframe/navigator-plugin'

// Create context to control global state
export const ContextDataPlugin = createContext<{
  data: any
  setData: (data: any) => void
}>(null as any)

// Create provider to pass global state
export const DataPluginProvider: React.FC = (props) => {
  const [data, setData] = useState<any>(null)
  return (
    <ContextDataPlugin.Provider value={{ data, setData }}>
      {props.children}
    </ContextDataPlugin.Provider>
  )
}

export const useDataPlugin = (): PluginType & {
  dataFromNextPage: (params: { from: string }) => any
} => {
  const context = useContext(ContextDataPlugin)

  return useMemo(() => {
    return {
      lifeCycleHooks: {
        onPoppedWithData: async ({ from, data }) => {
          // control state of plugin in lifecycle hook
          context.setData({ [from]: data })
        },
      },
      // dataFromNextPage would be extracted to access state of plugin for component
      dataFromNextPage: ({ from }: { from: string }) => context?.data?.[from],
    }
  }, [context])
}

export const dataPlugin: NavigatorPluginType = {
  name: 'dataPlugin',
  provider: DataPluginProvider,
  executor: useDataPlugin,
}

You could use context api to control state of plugin from component.

In this example, this Provider would wrap Navigator to pass states.

And then you could access states of plugin from component.

Apply plugin

App.tsx

import { dataPlugin } from './plugins'

const App: React.FC = () => {
  return (
    <Navigator plugins={[dataPlugin]}>
      <Screen path="/" component={Main} />
      <Screen path="/other" component={Other} />
    </Navigator>
  )
}

Main.tsx

import { useDataPlugin } from './plugins'

const Main: React.FC = () => {
  const { dataFromNextPage } = useDataPlugin()
  const result = useMemo(
    () => dataFromNextPage({ from: '/other' }),
    [dataFromNextPage]
  )

  return (
    <div>
      <span>Main</span>
      <span>{result}</span>
    </div>
  )
}

Apply middleware to lifecycle hook

Define plugin

import type {
  BeforePushType,
  NavigatorPluginType,
  PluginType,
} from '@karrotframe/navigator-plugin'

// composeMiddlewares compose multiple middlewares for lifecycle hook.
import { composeMiddlewares } from '@karrotframe/navigator-plugin'

const filterPathMiddleware = async (
  ctx: BeforePushType,
  next: () => Promise<BeforePushType | void>
): Promise<BeforePushType | void> => {
  if (ctx.to === 'not valid') {
    // You could pass custom value as argument to next middleware
    await next({
      ...ctx,
      to: 'valid',
    })
  }
  // This next middleware would get basic arguments of lifecycle hook
  // because there is not any argument because there is not any argument for next function.
  await next()
}
const loggerMiddleware = async ({
  to,
}: BeforePushType): Promise<BeforePushType | void> => {
  console.log('to: ', to)
}

export const pluginWithMiddleware: NavigatorPluginType = {
  name: 'pluginWithMiddleware',
  executor: (): PluginType => ({
    lifeCycleHooks: {
      beforePush: composeMiddlewares<BeforePushType>([
        filterPathMiddleware,
        loggerMiddleware,
      ]),
    },
  }),
}

You could call next function as second argument of callback function when you use composeMiddlewares for hook.

You could control lifecycle hook by stages with middleware.

And next function could take custom value to pass this value to next middleware.

Lifecycle Hooks

beforePush

This hook calls callback function before push()

nametypedescriptionexample
toStringRoute path by push()."/product"
screenInstancesIScreenInstance[]Array that contains screen instance info from client
screenInstancePtrnumberThe pointer to indicate current screen3
optionsOptionspush, replace, pop could be called from callback function

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onPushed

This hook calls callback function immediately before route event(push).

nametypedescriptionexample
toStringRoute path by push()."/product"
screenInstancesIScreenInstance[]Array that contains screen instance info from client
screenInstancePtrnumberThe pointer to indicate current screen3
optionsOptionspush, replace, pop could be called from callback function

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeReplace

This hook calls callback function before replace()

nametypedescriptionexample
toStringRoute path by replace()"/account"
optionsOptionspush, replace, pop could be called from callback function

onReplaced

This hook calls callback function immediately before route event(replace).

nametypedescriptionexample
toStringRoute path by replace()"/account"
optionsOptionspush, replace, pop could be called from callback function

beforePop

This hook calls callback function before pop()

nametypedescriptionexample
fromStringRoute path by pop()"/product/1"
screenInstancesIScreenInstance[]Array that contains screen instance info from client
screenInstancePtrnumberThe pointer to indicate current screen3
optionsOptionspush, replace, pop could be called from callback function

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onPopped

This hook calls callback function before go back with provided depth(backwards count)

nametypedescriptionexample
fromStringRoute path by pop()"/product/1"
screenInstancesIScreenInstance[]Array that contains screen instance info from client
screenInstancePtrnumberThe pointer to indicate current screen3
optionsOptionspush, replace, pop could be called from callback function

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onPoppedWithData

This hook calls callback function when pop().send() is called.

nametypedescriptionexample
fromStringRoute path by pop()"/product/1"
dataobjectThe data as argument of pop().send(){ name: 'John Doe' }
screenInstancesIScreenInstance[]Array that contains screen instance info from client
screenInstancePtrnumberThe pointer to indicate current screen3
optionsOptionspush, replace, pop could be called from callback function

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeRegisterScreen

Each screen is registered during render by ReactDOM,

if Screen components are defined as child component of Navigator component.

This hook calls callback function before register this screens.

nametypedescriptionexample
screenIScreenThe object that contains screen info as props for Screen component
screensIScreen[]Array that contains each screen info as child components of Navigator component

screen

{
   id: '/main',
   path: '/main',
   component:  Main
}

onRegisterScreen

Each screen is registered during render by ReactDOM,

if Screen components are defined as child component of Navigator component.

This hook calls callback function after register this screens.

nametypedescriptionexample
screenIScreenThe object that contains screen info as props for Screen component
screensIScreen[]Array that contains each screen info as child components of Navigator component. This array also contains screen object above.

screen

{
   id: '/main',
   path: '/main',
   component:  Main
}

beforeInsertScreenInstance

ScreenInstance would be instantiated with screen info which is registered,

when any route event like push is triggered.

This hook calls callback function before such ScreenInstance is registered.

nametypedescriptionexample
screenInstanceIScreenInstancescreen instance info from client
screenInstancesIScreenInstance[]Array that contains screen instance info from client
ptrnumberThe pointer that indicates current screenInstance5
optionsOptionssetScreenInstances, setScreenInstancePtr could be called from callback function

screenInstances

;[
  {
    id: 4,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onInsertScreenInstance

ScreenInstance would be instantiated with screen info which is registered,

when any route event like push is triggered.

This hook calls callback function just after such ScreenInstance is registered.

nametypedescriptionexample
screenInstanceIScreenInstancescreen instance info from client
screenInstancesIScreenInstance[]Array that contains screen instance info from client
ptrnumberThe pointer that indicates current screenInstance5
optionsOptionssetScreenInstances, setScreenInstancePtr could be called from callback function

screenInstances

;[
  {
    id: 4,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeMapScreenInstance

You would need to modify properties of screenInstance in specific case with mapper function.

This hook calls callback functions before mapper function that will modify screenInstance is called

nametypedescriptionexample
screenInstancesIScreenInstance[]Array that contains screen instance info from client
ptrnumberThe pointer that indicates current screenInstance4
optionsOptionsmapperScreenInstance could be called in callback function

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onMapScreenInstance

You would need to modify properties of screenInstance in specific case with mapper function.

This hook calls callback functions right after mapper function that will modify screenInstance is called

nametypedescriptionexample
screenInstancesIScreenInstance[]Array that contains screen instance info from client
ptrnumberThe pointer that indicates current screenInstance4
optionsOptionsmapperScreenInstance could be called in callback function

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeAddScreenInstancePromise

resolve function of the promise for push will be declared to an object, screenInstancePromiseMap,

when push event is triggered or pop() event which includes pop().send() is triggered.

This hook calls callback function before initializing screenInstancePromiseMap by screenInstanceId with resolve function

nametypedescriptionexample
screenInstanceIdstringscreenInstanceId which is matched with screenInstancePromise
screenInstancesIScreenInstance[]Array that contains screen instance info from client
screenInstancePtrnumberThe pointer to indicate current screen4
screenInstancePromiseIScreenInstancePromiseThe object to save resolve function for screenInstance with screenInstanceId

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onAddScreenInstancePromise

resolve function of the promise for push will be declared to an object, screenInstancePromiseMap,

when push event is triggered or pop() event which includes pop().send() is triggered.

This hook calls callback function right after initializing screenInstancePromiseMap by screenInstanceId with resolve function

nametypedescriptionexample
screenInstanceIdstringscreenInstanceId which is matched with screenInstancePromise
screenInstancesIScreenInstance[]Array that contains screen instance info from client
screenInstancePtrnumberThe pointer to indicate current screen4
screenInstancePromiseIScreenInstancePromiseThe object to save resolve function for screenInstance with screenInstanceId

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onMountNavbar

Props of ScreenHelmet are declared to set up App bar of top from @karrotframe/navigator.

A navbar should be mounted internally to render this App Bar.

This hook calls callback function when the navbar is mounted.

nametypedescriptionexample
screenHelmetPropsIScreenHelmetPropsscreenInstanceId which is matched with screenInstancePromise

screenHelmetProps

{
  appendLeft: "button",
  appendRight: {
    $$typeof: Symbol(react.element)
    key: null
    props: {children: '', onClick: ƒ}
    ref: null
    type: "div"
  },
  closeButtonLocation: "left",
  customBackButton: null,
  customCloseButton: null,
  disableScrollToTop: false,
  noBackButton: false,
  noBorder: false,
  noCloseButton: false,
  onTopClick: () => { console.log("hello world"); },
  preventSwipeBack: false,
  title: "Main",
  visible: true,
}

onUnmountNavbar

Props of ScreenHelmet are declared to set up App bar of top from @karrotframe/navigator.

A navbar should be mounted internally to render this App Bar.

This hook calls callback function when the navbar is unmounted.

nametypedescriptionexample
screenHelmetPropsIScreenHelmetPropsscreenInstanceId which is matched with screenInstancePromise

screenHelmetProps

{
  appendLeft: "button",
  appendRight: {
    $$typeof: Symbol(react.element)
    key: null
    props: {children: '', onClick: ƒ}
    ref: null
    type: "div"
  },
  closeButtonLocation: "left",
  customBackButton: null,
  customCloseButton: null,
  disableScrollToTop: false,
  noBackButton: false,
  noBorder: false,
  noCloseButton: false,
  onTopClick: () => { console.log("hello world"); },
  preventSwipeBack: false,
  title: "Main",
  visible: true,
}

Interfaces

type NavigatorPluginType = {
  name: string
  provider?: React.FC
  executor: () => PluginType
}
interface IScreen {
  id: string
  path: string
  Component: React.ComponentType
}
interface IScreenInstance {
  id: string
  screenId: string
  nestedRouteCount?: number
  present: boolean
  as: string
}
interface IScreenInstancePromise {
  resolve: (data: any | null) => void
}
interface IScreenInstancePromise {
  title?: React.ReactNode
  appendLeft?: React.ReactNode
  appendRight?: React.ReactNode
  closeButtonLocation?: 'left' | 'right'
  customBackButton?: React.ReactNode
  customCloseButton?: React.ReactNode
  noBorder?: boolean
  disableScrollToTop?: boolean
  onTopClick?: () => void
  visible?: boolean
  preventSwipeBack?: boolean
  noBackButton?: boolean
  noCloseButton?: boolean
}

FAQs

Package last updated on 23 Mar 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts