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";
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]);
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};
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;
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)