Files
flipper/docs/writing-a-plugin.md
Daniel Büchele 43157f974e Update docs to use package name as ID
Summary: We swtiched from using the name of the package as the ID which is used to identify a client plugin. These changes were not reflected correctly in our docs.

Reviewed By: jknoxville

Differential Revision: D14165439

fbshipit-source-id: 1cbb9c1723911f8fa4b7df19c631e6f260c81bd8
2019-02-21 09:39:54 -08:00

4.6 KiB

id, title, sidebar_label
id title sidebar_label
writing-a-plugin Writing a plugin in JavaScript Writing a plugin

Every plugin needs to be self-contained in its own folder. At the root-level of this folder we are expecting a package.json for your plugin and an index.js file which is the entry-point to your plugin. To learn more about the basic setup of a plugin, have a look at JavaScript Setup.

We expect this file to have a default export of type FlipperPlugin. A hello-world-plugin could look like this:

import {FlipperPlugin} from 'flipper';

export default class extends FlipperPlugin {
  render() {
    return 'hello world';
  }
}

persistedState

React state will be gone once your plugin unmounts. Every time the user switches between plugins, the React state will be reset. This might be fine for UI state, but doesn't make sense for your plugin's data. To persist your data when switching between plugins, you can use our persistedState-API.

Flipper passes a prop with the current persistedState to your plugin. You can access it via this.props.persistedState. To changes values in the persistedState you call this.setPersistedState({...}). Our API works similar to React's state API. You can pass a partial state object to setPersistedState and it will be merged with the previous state. You can also define a static defaultPersistedState to populate it.

A common pattern for plugins is to receive data from the client and store this data in persistedState. To allow your plugin to receive this data, even when the UI is not visible, you can implement a static persistedStateReducer. This reducer's job is to merge the current persistedState with the data received from the device. Flipper will call this method, even when your plugin is not visible and the next time your plugin is mounted, persistedState will already have the latest data.

static defaultPersistedState = {
  myData: [],
}

static persistedStateReducer = (
  persistedState: PersistedState,
  method: string,
  newData: Object,
): PersistedState => {
  // Logic to merge current state with new data
  return {
    myData: [...persistedState.myData, newData],
  };
};

Have a look at the network plugin to see this in action. To send data while your plugin is not active, you need to opt-in on the native side of your plugin. For more info around this, read the mobile setup.

Notifications

Plugins can publish notifications which are displayed in Flipper's notification panel and trigger system level notifications. This can be used by your plugin to make the user aware of something that happened inside the plugin, even when the user is not looking at the plugin right now. The network plugin uses this for failed network requests. All notifications are aggregated in Flipper's notifications pane, accessible from the sidebar.

A notification should provide actionable and high-signal information for important events the user is likely to take action on. Notifications are generated from the data in your persistedState. To trigger notifications you need to implement a static function called getActiveNotifications. This function returns an array of all currently active notifications. To invalidate a notification, you simply stop including this notification in the array you are returning.

type Notification = {|
  id: string, // used to identify your notification and needs to be unique to your plugin
  title: string, // title shown in the system-level notification
  message: string, // detailed information about the event
  severity: 'warning' | 'error',
  timestamp?: number, // unix timestamp of when the event occurred
  category?: string, // used to group similar notifications (not shown to the user)
  action?: string, // passed to your plugin when navigating from a notification back to the plugin
|};

static getActiveNotifications = (
  persistedState: PersistedState,
): Array<Notification> => {
  return persistedState.myData.filter(d => d.error).map(d => {
    id: d.id,
    title: 'Something is rotten in the state of Denmark',
    message: d.errorMessage,
    severity: 'error',
  });
};

When the user clicks on a notification, it links back into your plugin. this.props.deepLinkPayload will be set to the string provided in the notification's action. This can be used to highlight the particular part of the data that triggered the notification. In many cases the action will be some sort of ID for your data. You can use React's componentDidMount to check if a deepLinkPayload is provided.