Hallo ๐Ÿ˜Š

Kamsi Oleka, a software engineer, a gym rat, and a stand-up comedian amongst friends.

I have worked with interesting companies like Thepeer, Klarna, Piggy LLC, and Abeg.

A blog of my thoughts, experiences, and cool stuff I've built.

Back

Building a React SDK

In React, a package/library could be anything, e.g. a component, multiple components. Usually packages exist to abstract some repetitive code and make it easier to use.

In this article, we're going to be focused on building a React package for the pay-script we had deployed. At the end of this, we'd be able to install the package, import it into any React codebase and load up the SDK.

Prerequisite

  • Understanding JavaScript basics, JavaScript functions, Closures
  • Knowledge of React

Step 1

Open up your terminal and make a new directory called pay-app-react

One simple way of creating a React library is by using create-react-library cli. Like create-react-app to React, so is create-react-library to React libraries.

To create a new library, simply run:

npx create-react-library pay-app-react
NB:

If you use a different library name from pay-app-react, replace all occurrences of pay-app-react with your chosen library name.

Some configuration options would follow after running the command in your terminal. For template, select typescript and for package manager, use npm. Every other thing can be altered from the package.json file.

Screenshot of terminal window!

After picking out the options, our module would be created.

You should have an example and src folder. The example folder houses the React app to test your new library with locally.

To get started, open up two tabs in your terminal. In one tab, run:

cd pay-app-react && npm start

and in the other, run:

cd pay-app-react/example && npm start

As you can see from the first terminal window, changes you make to the library are being watched and automatically bundled to reflect on the example app.

Step 2

Set up the repo for our library

We need to clean up our src folder and create files we will need.

Delete all files bar the .eslintrc and index.ts file and run the following command in your terminal:

Step 3

Declare types for our Props and Events

We need to define the types for our expected props and response responses received in our callbacks. Open up the types.ts file and paste the following into it

cd src && touch types.ts

Open the types.ts file and add the code below

// types.ts

export interface EventResponse {
  type: string;
  data: undefined | Object;
}

export interface Props {
  publicKey: string;
  amount: string | number;
  userReference: string;
  currency: string;
  onSuccess: (response: EventResponse) => void;
  onError: (response: EventResponse) => void;
  onClose: (response: EventResponse) => void;
}

export type NestedFuncs = {
  setup: () => void;
  open: () => void;
};

Step 4

Let's create our script hook

touch script.ts

Open the script.ts file and add the code below

// script.ts

import { useEffect, useState } from "react";

type ScriptStatusType = {
  loaded: boolean;
  error: boolean;
};

// url of your hosted script, I'll use the open we've hosted at thepeer to save time. You can use it too
const payJS = "https://cdn.thepeer.co/v1/chain.js";
// id could be anything you want
// We're really just trying to identify our own tag right away and do whatever we want with it,
// as opposed to gathering all script tags in the document
const id = "payScript";

export default function useScript() {
  const [state, setState] = useState<ScriptStatusType>({
    loaded: false,
    error: false,
  });

  useEffect(() => {
    const scriptTag = document.getElementById(id);
    const scriptSrc = scriptTag && scriptTag.getAttribute("src");

    if (scriptSrc)
      return setState({
        loaded: true,
        error: false,
      });

    const script = document.createElement("script");
    script.id = id;
    script.src = payJS;
    script.async = true;

    const onScriptLoad = () => {
      setState({
        loaded: true,
        error: false,
      });
    };

    const onScriptError = () => {
      setState({
        loaded: true,
        error: true,
      });
    };

    script.addEventListener("load", onScriptLoad);
    script.addEventListener("complete", onScriptLoad);
    script.addEventListener("error", onScriptError);

    document.body.appendChild(script);

    return () => {
      script.removeEventListener("load", onScriptLoad);
      script.removeEventListener("complete", onScriptLoad);
      script.removeEventListener("error", onScriptError);
    };
  }, []);

  return [state.loaded, state.error];
}

The hook above checks if our script exists in the document by looking for a tag with our id, if it does, it updates the state appropriately and the returns;

In a case where it does not exist in the document, it creates a tag to load asynchronously with our id attached to it. Adds event listeners for various states to the tag before subsequently adding it to the document.

Don't forget to clean up your listeners now.๐Ÿ™ƒ

Step 5

Let's create the usePay file

touch usePay.tsx
// usePay.tsx

import { useEffect } from "react";
import useScript from "./script";
import { Props, NestedFuncs } from "./types";

declare const window: Window &
  typeof globalThis & {
    Pay: (config: Props) => NestedFuncs;
  };

const usePay = (props: GeneralProps & SendProps) => {
  const [loaded, error] = useScript();

  useEffect(() => {
    if (error) throw new Error("Unable to load usePay modal");
  }, [error]);

  return () => {
    if (error) throw new Error("Unable to load usePay modal");

    if (loaded) {
      // the code commented below should be used if you hosted the script file from the previous tutorials, but since we're using an already hosted script file, we have to call the right constructors
      // const pay = window.Pay && window.Pay(props)
      const pay = window.ThePeer && window.ThePeer.send(props);
      pay.setup();
      return pay.open();
    }
  };
};

export default usePay;

A few things are going on here. I'll try to break it all down carefully

  • We declare the constructor we're expecting our script to inject into the browser instance on our window with its props (Just some TS things๐Ÿ˜ƒ).

  • We create the usePay function, it calls our hooks and returns the error and loaded state values. A few things happen next:

    • we throw an error from our useEffect if the error state is true and;
    • we form a closure that is returned when our usePay hook is called in a React app. This function can then be executed in a button's click handler.

Step 6

Import the usePay function into the index file and re-export it. Our entry point was set to this in the package.json file. If you do not do this, don't forget to change the path to whatever entry point your want.

// index.ts

export { default as usePay } from "./usePay";

Step 7: Testing locally

Run the command below to build your library, install it and run it from your example app in a browser.

npm run build && cd example && npm i pay-app-react && npm start

You should see this in the browser Screenshot of the React SDK in the browser!

To publish to NPM

  1. Create an account if you don't already have one
  2. After creating the account, open your terminal and run
npm login
  1. After logging in, run
  npm run prepare && npm publish
  1. Fill out the questions asked and viola!