Using Facebook SDK with React the right way
Tags: react, javascript, typescript, webdev
Published: 2021-07-13 07:51:40

Recently I joined [KBC Bank & Verzekering](https://www.kbc.be) as part of a new, small internal marketing team. As a full stack developer, I'm responsible for building the (web) applicatons required for marketing campaigns. One of these campaigns required logging in with Facebook. I had never worked with Facebook integration before, but I was convinced it wouldn't be too hard.

Boy, was I wrong.

# 3rd party libraries

React is a library built by Facebook, so it should have its own library for Facebook integration, right? Wrong. Ironically enough, Facebook only has an [SDK for JavaScript](https://developers.facebook.com/docs/javascript). (Yes, vanilla JavaScript, not even TypeScript)
There used to be an [SDK for React Native](https://developers.facebook.com/docs/react-native), but apparently that's no longer being supported either.

This means we're on our own.

My first instinct was to look for 3rd party libraries. A library that immediately caught my attention was [react-facebook-login](https://www.npmjs.com/package/react-facebook-login). I tried it and it did what it had to do, but its functionality was very limited and the library hadn't been updated in years, while having 71 open issues and 5 open pull requests at the time of writing.

Other libraries, like [react-facebook](https://www.npmjs.com/package/react-facebook), showed similar patterns. I wasn't convinced.

# Doing it ourselves

As Napoléon Bonaparte once said: if you want something done, do it yourself.

We could just add the SDK for JavaScript to the `index.html`, but that's not something we want to do. We might not want to include the SDK on every page. Instead, it might be a good idea to build a wrapper for the SDK using React Hooks. This gives us access to all functionality provided by Facebook, while still keeping full control.

## Loading the script

The first step is loading the script into the page, so let's create a new React Hook that does just that:

```javascript
const useFacebook = () => {
  useEffect(() => {
    const script = document.createElement("script");

    script.async = true;
    script.defer = true;
    script.crossOrigin = "anonymous";
    script.src = `https://connect.facebook.net/nl_BE/sdk.js`;

    document.body.appendChild(script);

    window.fbAsyncInit = function () {
      FB.init({
        appId: <ENTER YOUR FACEBOOK APP ID HERE>,
        autoLogAppEvents: true,
        xfbml: true,
        version: "v11.0",
      });
    };

    return () => {
      delete window.fbAsyncInit;
      document.body.removeChild(script);
    };
  }, []);
};
```

As you can see, we also remove the script as soon as the hook is not in use anymore.

If you're using TypeScript, you might already run into some issues: we're setting the `fbAsyncInit` property on `window`, which doesn't have that property to begin with. We need to add this property to the [Window](https://developer.mozilla.org/en-US/docs/Web/API/Window) interface by adding the following code:

```javascript
declare global {
  interface Window {
    fbAsyncInit?: Function;
  }
}
```

Another issue you might run into is that TypeScript is unaware of the `FB` variable that gets inserted by the script. You can fix this by adding `@types/facebook-js-sdk` to your project:

```bash
npm install @types/facebook-js-sdk --save-dev
```

We should be able to follow along with the [official docs](https://developers.facebook.com/docs/facebook-login/web) now.

## Putting the SDK to use

The SDK makes use of callbacks, but we're fancy, so we are going to turn them into promises.

Let's illustrate this with the `FB.login` function. By default, the `FB.login` function returns a response, regardless of the result. Let's turn this into a promise that returns the response if the login is successful and throws an error otherwise:

```javascript
  const login = () => {
    return new Promise<fb.StatusResponse>((resolve, reject) => {
      FB.login((response) => {
        if (response.status === "connected") resolve(response);
        else reject(new Error("Facebook login failed"));
      });
    });
  };
```

In case you're wondering where `fb.StatusResponse` comes from: it's a type used in TypeScript that comes from `@types/facebook-js-sdk`.

Et voilà! You should have successfully integrated the Facebook SDK in your React application, the right way.