React Pluggable: Thinking Features over Components

React Pluggable: Thinking Features over Components

·

4 min read

React Pluggable: Thinking Features over Components

React Pluggable is a JS library that brings plugin-based development to React & JavaScript. While it may contradict the philosophy of React, it has worked well for creators and has solved problems at scale. The idea revolves around the open-closed principle. This principle states that systems should be open for extension but closed for modification. It gives us three main benefits:

  • Features can be developed & tested in isolation (features can have an interface, provide or consume data & functions, interact with external APIs and more)
  • A plugin can be added or removed by a single line (which is perfect for A/B testing)
  • Ships with dependency management: Plugins can depend on other plugins and communicate with each other

Motivation:

While working on BuilderX, the team at GeekyAnts realised that adding and removing features was becoming cumbersome. Furthermore, a lot of knowledge transfer was required for a new member to contribute. This slowed down the overall speed of development. To overcome this hurdle, they came up with a very basic version of what React Pluggable is today. As the project progressed, the library slowly evolved while remaining generic for all use cases. Currently, v3 of BuilderX is completely based on this pattern.

Inspiration:

The first inspiration came from the extension system of VS Code. VS Code extensions are simple modules that can be written independently and can change the overall look and functionalities of the editor. Another example of a similar pattern was that of Laravel's Service Provider pattern. In Laravel projects, developers can harness the functionalities of a provider by simply adding it to the list of providers in config/app.php. All the implementation is written inside the provider itself and has nothing to do with the external app. React Pluggable aims to provide that level of ease in development for all applications.

Features:

  • Isolated & abstracted development

    Just like how a provider is developed in isolation and can be plugged anywhere, React Pluggable can be developed in a modular manner and then inserted anywhere. As long as the dependencies are met, it can be reused in multiple projects by simply copy-pasting the code or publishing the plugin on NPM. It also allows coders to test a feature in complete isolation.

  • Adding/Removing features by simply adding/removing a single line

    A plugin can be added in the app by simply installing it in the pluginStore. Once installed, the activate function of the plugin starts running. All the operations and components defined are added. This allows developers to control a feature by just manipulating one line. In case we want to remove a feature or upgrade it with a different implementation, all we need to do is modify the installation and voila! Everything works as expected!

      // Installing MyAuthPlugin
      pluginStore.install(new MyAuthPlugin());
    
  • Dependency Management & Dependency Injection (runtime)

    Once installed, a plugin can be used or installed anywhere that the plugin store is available. This allows us to have more control on what is added to an app, when it is added and where it is used.

      // inside activate of AuthPlugin
      activate(): void {
          this.pluginStore.addFunction("Auth.authenticate", () => {
            this.pluginStore.install(new TodoPlugin());
          });
    
          this.pluginStore.addFunction("Auth.logout", () => {
            this.pluginStore.uninstall("Todo");
          });
      }
    
  • Renderer Plugin

    Renderer Plugin is an inbuilt plugin provided by the package. It can be used to render components wherever a slot is created. A slot can be created by simply using:

      <Renderer placement="header" />
    

    This allows us to add components at any level and at any given position, making component rendering very easy.

  • Events

    React Pluggable ships with an event system that can be used as a communication channel between the plugins to broadcast and listen to events. Here is a quick example of an Auth plugin.

    Adding an Event to pluginStore

      activate(): void {
         this.pluginStore.addEventListener("Auth.authentication", (event: any) => {
            console.log(event);
         });
      }
    

    Dispatching the Event

      pluginStore.dispatchEvent(new Event("Auth.authentication"));
    
  • Works well with React

      // Creating pluginStore
      const pluginStore: PluginStoreWithPlugins = createPluginStore();
    
      // Using provider
      <PluginProvider pluginStore={pluginStore}>
         <Renderer placement="root" />
      </PluginProvider>
    
      // Using pluginStore with React components
      this.pluginStore.executeFunction("Renderer.add", "header-right", () => (
         <ShareDialogContainer axiosInstance={axiosInstance} />
      ));
    

React Pluggable has been rigorously tested during the development of BuilderX. It has truly helped GeekyAnts make the development of such a complex app a lot simpler. We now want this to help the developer community code more complex apps in a far simpler and feature-oriented way. We created a stock monitoring app that showcases data in graphs. Graph features were added to the app using the plugin system. If you wish to know more about the library, you can visit the official documentation.

React-Pluggable-bnr-1200-630.png

This post was written by Aditya J and Amar S . Edited by Kavya V.