DEV Community

Robert Marshall
Robert Marshall

Posted on • Originally published at robertmarshall.dev on

Dynamically set an Account ID in Stripe using loadStripe

I recently had to integrate Stripe into a React project. It seemed like it would be fairly simple. The Stripe React documentation seemed really well put together, and looked like the components just needed plugging in and styling. Perfect!

Unfortunately that was not the case for my particular requirement.

The Brief: Why dynamically set a Stripe account ID

An API call fetches a particular Stripe account ID depending on the business that will be paid. This account ID will be passed to the Stripe package. This means that that account ID is not available immediately on page load.

The Problem: Limitations with loadStripe in React

The Stripe documentation outlines many different payment methods. But all examples come back to the same implementation requirement.

Make sure to call loadStripe outside of a component’s render to avoid recreating the Stripe object on every render.

That isn’t the best news, as the account ID will be passed in as a prop and dynamically added to the loadStripe function. It does not exist when the page is first loaded.

When I tried it within the component render, I received an error:

Unsupported prop change on Elements: You cannot change the stripe prop after setting it.

So, it isn’t just a case of avoiding rerendering. It simply cannot be done like this.

The Solution: How to use useEffect and useState with Stripe in React

There needs to be a way to pass an account ID into a component as a prop. Then pass this to the Stripe React loadStripe function dynamically. And only run this function once. This will then pass the Stripe object to the Elements and CardElement component.

So how can this be done?

The example below is a very simple implementation that will allow you to pick apart and use as you need.

A lot of the examples show using loadStripe direct from the @stripe/stripe-js package. To make it work the way we need, loadStripe needs to be imported from the @stripe/stripe-js/pure module.

import React, { useEffect, useState } from "react";
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js/pure";
Enter fullscreen mode Exit fullscreen mode

Then we need to get the Stripe object from loadStripe asynchronous and save it into the state. For this use a useEffect function.

const [stripeObject, setStripeObject] = useState(null);

  // This function will re-run if the accountId prop changes.
  useEffect(() => {
    const fetchStripeObject = async () => {
      // If there is no accountId, do not run the loadStripe function.
      if (accountId) {
        const res = await loadStripe(
          YOUR_PUBLISHABLE_KEY,
          {
            stripeAccount: accountId
          }
        );
        // When we have got the Stripe object, pass it into our useState.
        setStripeObject(res);
      }
    };
    fetchStripeObject();
  }, [accountId]);

Enter fullscreen mode Exit fullscreen mode

In this code snippet a useState function is being set which gives the ability to save the Stripe object once received.

A useEffect function is then used to listen to the accountId prop. When this prop changes the useEffect function will rerender.

However, the loadStripe function only wants to fire when there is an accountId to use, so this is wrapped in an if statement.

Once the loadStripe function returns the Stripe object, it is saved into the stripeObject to be used later.

This piece of code can then be rounded off by adding a conditional render. If there is no Stripe object – show a loading screen. Otherwise set the Element component.

// If no Stripe object, do not render the Stripe Element.
if (!stripeObject) {
   return Loading...;
}

// Once we have the Stripe object, load everything.
return {children};
Enter fullscreen mode Exit fullscreen mode

This wrapper can then be used around the payment components provided by Stripe within the @stripe/react-stripe-js package. The whole wrapper would look like this:

import React, { useEffect, useState } from "react";
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js/pure";

function StripeWrapper({ accountId, children }) {
  const [stripeObject, setStripeObject] = useState(null);

  // This function will re-run if the accountId prop changes.
  useEffect(() => {
    const fetchStripeObject = async () => {
      // If there is no accountId, do not run the loadStripe function.
      if (accountId) {
        const res = await loadStripe(
          "pk_test_JJ1eMdKN0Hp4UFJ6kWXWO4ix00jtXzq5XG",
          {
            stripeAccount: accountId
          }
        );
        // When we have got the Stripe object, pass it into our useState.
        setStripeObject(res);
      }
    };
    fetchStripeObject();
  }, [accountId]);

  // If no Stripe object, do not render the Stripe Element.
  if (!stripeObject) {
    return Loading...;
  }

  // Once we have the Stripe object, load everything.
  return {children};
}

export default StripeWrapper;
Enter fullscreen mode Exit fullscreen mode

For a full working example of this, take a look at the repo I put together. A quick clone will help show it fully implemented.

https://github.com/robmarshall/stripe-react-dynamic-account-id

I hope this helped get you moving. Let me know if I can be anymore help by contacting me on twitter @robertmars

Top comments (0)