How to solve Stripe elements for react causing memory leak on redirect

Viewed 508

I have a component where I'm giving my users an option to subscribe to remove ads from the app. I integrated Stripe perfectly until I came to the redirect portion of the app. Since I don't want to have people create multiple subscriptions if my users have an active subscription (which I'm getting from firebase db), then I want to redirect my users to another page. This is causing me to see a warning about memory leaks once the redirect takes place. This doesn't happen anymore after trying to navigate back to the subscription component. Here's my component stripped away with most code that doesn't cause the error to show. I figured it was stripe elements because If I remove the call to that component, then all works great, and no memory leaks. Has anyone ever experienced this issue and have a fix?

I feel like it has to do with the way I'm setting the show form using the React.useEffect hook. Any help is truly appreciated!

import React, { Component } from 'react';
import { compose } from 'recompose';
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import { Redirect } from 'react-router-dom';
import StripeForm from '../components/StripeForm';
import Grid from '@material-ui/core/Grid';


import {
    AuthUserContext,
    withAuthorization,
    withEmailVerification,
} from '../components/Session';
import { withFirebase } from '../components/Firebase';
const stripePromise = loadStripe('stripe-key-here');



const AccountPage = (props) => {
    const show = props.firebase.userData.stripe.subscriptions.subscription.plan.active
    const [showForm, setShowForm] = React.useState(show)

    React.useEffect(() => {
        if (show !== showForm) {
            setShowForm(show);
        }
    }, [show, showForm])

    return (

        <AuthUserContext.Consumer>
            {authUser => {
                return (
                    <>
                        {showForm ? <Redirect to="/thank-you" /> : null}
                        <Grid container spacing={3} style={{ background: '#fff', maxWidth: '94%', margin: '20px auto', borderRadius: 7 }}>
                            <Grid item xs={12} sm={6} style={{ padding: '20px 50px', textAlign: 'justify', }}>
                                <UpgradeMessage />
                            </Grid>
                            <Grid item xs={12} sm={6} style={{ padding: '20px 50px' }}>
                                <div style={{ textAlign: 'justify' }}>
                                    <h3>Purchase a subscription for $9/year!</h3>
                                    <p>Purchasing a subscription will remove the ads from the app and will help us stay up and running!</p>
                                </div>
                                <Elements stripe={stripePromise}>
                                    <div style={{ padding: 15, borderRadius: 10, border: 'solid 4px #fafafa' }}>
                                        <StripeForm setShowForm={setShowForm} firebase={props.firebase} authUser={authUser} />
                                    </div>
                                </Elements>
                            </Grid>
                        </Grid>
                    </>
                )
            }
            }
        </AuthUserContext.Consumer>
    )
};

class LoginManagementBase extends Component {
    constructor(props) {
        super(props);

        this.state = {
            activeSignInMethods: [],
            error: null,
        };
    }

    render() {
        return (
            <div>
                <button onClick={() => this.props.firebase.doSignOut()}>Log out of this app!!!</button>
            </div>
        );
    }
}

const LoginManagement = withFirebase(LoginManagementBase);

const condition = authUser => !!authUser;

export default compose(
    withEmailVerification,
    withAuthorization(condition),
    withFirebase,
)(AccountPage);
1 Answers

You will probably need to look into StripeForm to fix this issue.

What happens in StripeForm after calling setShowForm?
I assume there is some code after setShowForm, but with the redirect from setShowForm being called: Grid, Elements and StripeForm components are unmounted and it gives you this warning.

If this is actually your issue, setShowForm should be the last thing you do in StripeForm when it's meant to redirect.

A similar case with Formik, where you have this warning:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

When you submit your form you call some action to save it, once it's done Formik needs to know it's done with setSubmitting(false);.
That's how it goes :

callSomeSaveAction(...)
.then(...)
.catch(...)
.finally(() => setSubmitting(false));

It works fine until you decide to use a redirect in callSomeSaveAction.then

This happens because the redirect unmount the formik component but setSubmitting is called anyway.

Hopefully you can fix you issue with something like this. Since I know that the component will be unmounted thanks to the redirect, I don't need setSubmitting(false) when I redirect, but I still need it in case an error occured:

callSomeSaveAction(...)
.then(...)
.catch(() => {
    ...
    setSubmitting(false);
});
Related