0.1.4 • Published 4 years ago

flipper-hooks v0.1.4

Weekly downloads
3
License
MIT
Repository
github
Last release
4 years ago

flipper-hooks

Allow Flipper Plugins to use React Hooks

Motivation

Flipper (aka Sonar) is an excellent platform for debugging mobile/desktop apps on iOS, Android (original Flipper SDK), Electron and Node.js (with flipper-sonar SDK for Electron and Node).

Visualize, inspect, and control your apps from a simple desktop interface. It's even included as standard in all recent React Native apps.

Flipper is easily extensible using the plugin API, but out of the box requires one to create legacy React Components, with all sorts of static properties and a heavily loaded set of instance properties.

To reduce cognitive load as well as get all of the benefits of React Hooks in creating your plugins, this package provides a very simple wrapper around your vanilla React Function Components to expose your modern streamlined component to Flipper in the legacy format that it expects.

A simple useFlipper hook is provided to give you all the features that you would have otherwise got by setting static properties or receiving unique properties, but in the format you are used to with React Hooks.

This package written in TypeScript so get all the benefits of static type checking.

Usage

Two simple steps to allow any React Function Component (FC) to be used as a FlipperPlugin

Add flipper-hooks to your package

yarn add flipper-hooks --save

Import and use the Flipper React Hook inside your Function Component

const { useFlipper, createFlipperPlugin } from 'flipper-hooks'

// inside 
const MyFunctionComponent: React.FC<{}> = () => {
    const {
        client,
        persistedState,
        setActiveNotifications,
        setPersistedStateReducer
    } = useFlipper()

 // use the above helpers and render your flipper plugin 
}

Wrap your Reace Function Component

The createFlipperPlugin function wraps a standard React.FC and returns a FlipperPlugin

export default createFlipperPlugin(MyFunctionComponent, 'plugin-my-id') 

API

createFlipperPlugin

createFlipperPlugin(component, id, options)
  • component React.FC<{}> The React Function Component that you want to wrap; we dont supply any props as you can get them all off the useFlipper hook if you need them

  • id string The unique identifier of the plugin

  • options FlipperPluginOptions See below

A similar function createFlipperDevicePlugin will be added in the future

useFlipper

const {
    // CORE PROPS
    id,
    client,
    logger,
    persistedSate
    setPersistedState,
    target,
    deepLinkPayload,
    selectPlugin,
    isArchivedDevice,
    selectedApp,
    setStaticView
    
    // REDUCER SETTERS
    setPersistedStateReduce,
    setActiveNotifications,
    setMetricsReducer,
    setExportPersistedState,
    setOnRegisterDevice
} = useFlipper()

Note only deconstruct the flipper props from UseFlipper that you need as in the Example below

Example

const SamplePlugin: React.FC<{}> = _ => {
  const {
    client,
    persistedState,
    setActiveNotifications,
    setPersistedStateReducer
  } = useFlipper()

  setPersistedStateReducer(
    (persistedState: PersistedState, method: string, payload: Message) => {
      if (method === 'log') {
        return { ...persistedState, receivedMessage: payload.message }
      }
      return persistedState
    }
  )

  setActiveNotifications((persistedState: PersistedState) =>
    persistedState.currentNotificationIds.map((x: number) => ({
      id: `test-notification:${x}`,
      message: 'Example Notification',
      severity: 'warning' as 'warning',
      title: `Notification: ${x}`
    }))
  )

  const [prompt, setPrompt] = useState(
    'Type a message below to see it displayed on the mobile app'
  )

  const [message, setMessage] = useState('')

  const sendMessage = async () => {
    const _params: DisplayMessageResponse = await client.call(
      'displayMessage',
      { message: message || 'Weeeee!' }
    )

    setPrompt('Nice')
  }

  return (
    <Container>
      <Text>{prompt}</Text>
      <Input placeholder="Message" onChange={e => setMessage(e.target.value)} />
      <Button onClick={sendMessage}>Send</Button>
      {persistedState.receivedMessage && (
        <Text> {persistedState.receivedMessage} </Text>
      )}
    </Container>
  )
}

FlipperPluginOptions

  title?: string | null
  category?: string | null
  icon?: string | null
  gatekeeper?: string | null
  entry?: string | null
  bugs?: {
    email?: string
    url?: string
  } | null
  keyboardActions?: KeyboardActions | null
  screenshot?: string | null
  defaultPersistedState?: any
  maxQueueSize?: number
  persistedStateReducer?: PersistedStateReducer<T> | null
  getActiveNotifications?: ((persistedState: T) => Array<Notification>) | null
  metricsReducer?: ((persistedState: T) => Promise<MetricType>) | null
  exportPersistedState?:
    | ((
        callClient: (method: string, params?: any) => Promise<any>,
        persistedState: T | undefined,
        store: ReduxState | undefined,
        idler?: Idler,
        statusUpdate?: (msg: string) => void
      ) => Promise<T | undefined>)
    | null
  onRegisterDevice?:
    | ((
        store: Store,
        baseDevice: BaseDevice,
        setPersistedState: (pluginKey: string, newPluginState: T | null) => void
      ) => void)
    | null

FlipperPluginOptions

  setPersistedStateReducer<T>(reducer: PersistedStateReducer<T>): void

  setActiveNotifications<T>(
    reducer: (persistedState: T) => Array<Notification>
  ): void

  setMetricsReducer<T>(
    reducer: (persistedState: T) => Promise<MetricType>
  ): void

  setExportPersistedState<T>(
    exporter: (
      callClient: (method: string, params?: any) => Promise<any>,
      persistedState: T | undefined,
      store: ReduxState | undefined,
      idler?: Idler,
      statusUpdate?: (msg: string) => void
    ) => Promise<TextDecoderOptions | undefined>
  ): void

  setOnRegisterDevice<T>(
    handler: (
      store: Store,
      baseDevice: BaseDevice,
      setPersistedState: (pluginKey: string, newPluginState: T | null) => void
    ) => void
  ): void

Roadmap

This package will do one thing only, allow Flipper to be used with simple React function components. If Flipper is ever updated to use hooks, this package will be archived or absorbed into the core.

Right now, the package supports application plugins. Once the basic API is confirmed, it will also support device plugins.

License

MIT

0.1.4

4 years ago

0.1.3

4 years ago

0.1.2

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago