3

I am trying to determine if a customer has an active subscription or not. To do this I am utilizing the following code:

const stripe = require('stripe')('some-api-key');

export default function Example(){
    // the user will automatically be considered non-subbed by default
    const [isSubscriber, setIsSubscriber] = useState(false)


    // grab the customer id from stripe
    async function get_customer_id() {

        const customers = await stripe.customers.search({
            query: `metadata[\'some-meta-data-key\']:\'some-meta-data-value\'`
        });
        return customers.data[0]['id']
    }


    // grab the list of active subscriptions from stripe
    async function customer_is_subscriber(){
        const subs = await stripe.subscriptions.list({
            status: 'active',
        });
        return subs
    }


    // determine if the customer id is in the list of active subscriptions. 
    // return true if so, false otherwise
    async function test_equality(){
        const customer_id = await get_customer_id();
        const subbed = await customer_is_subscriber();

        const answer = subbed.find(sub => sub.customer === customer_id)
        return !!answer;
        
    }


    useEffect( () => {
        async function load_result() {
            const promise_function_return = await test_equality()
            setIsSubscriber(promise_function_return)
        }
        load_result();
}, [isSubscriber]);


return (

// some react code

)

}


I have been able to successfully get all of my other functions where I am doing the comparisons for if a user is a subscriber but where I am having an issue is updating the state value (e.g. true if they are subbed, false otherwise).

I found some good past questions on this specific topic such as: here The useState set method is not reflecting a change immediately

here: setState inside Promise in React

and here: setState inside a Promise function in a useEffect with hooks?

but I just have not been able to get it working correctly. This is currently the closest I have been able to get to solving this problem.

  • note your `async function customer_is_subscriber() { ... }` uses an `async-await` anti-pattern. it is exactly the same as `function customer_is_subscriber() { return stripe.subscriptions.list(...) }` – Mulan Apr 13 '22 at 05:26

2 Answers2

2

Currently your code says that, when isSubscriber changes, it should check if the user is a subscriber and update the isSubscriber state... so it's a chicken and egg problem. It won't set isSubscriber until isSubscriber gets set.

I think you want to change }, [isSubscriber]); to }, []); so that that code executes when the component first loads (not when isSubscriber changes).

Rocky Sims
  • 3,523
  • 1
  • 14
  • 19
  • Doesn't the useEffect hook will always run on mount regardless of if there is anything in its dependency array? Your suggestion definitely improves the code, but how does that solve the problem if useEffect is being run anyway? Am I wrong about when the useEffect hook is run? Do you know, Rocky? Did this actually fix your problem wannabeprogrammer, or was it something else? – Andrew Hulterstrom Apr 13 '22 at 01:02
  • 1
    Looks like you are correct. `useEffect` does alway run on mount even if dependency array is not empty. Oddly, nothing seems to be wrong. I got his code working (except with stripe calls replaced with hardcoded data) and it seems to set `isSubscriber` correctly. https://codesandbox.io/s/loving-buck-wl45tt?file=/src/App.js – Rocky Sims Apr 13 '22 at 01:25
2

The useEffect hook will always run on mount regardless of if there is anything in its dependency array. This means that your useEffect will work as is, and will run onMount as well as when isSubscriber changes:

    useEffect( () => {
        async function load_result() {
            const promise_function_return = await test_equality()
            setIsSubscriber(promise_function_return)
        }
        load_result();
}, [isSubscriber]);

To verify this, you can check out this codesandbox example. The useEffect looks just like yours, and you will notice that isSubscriber is initially set to false, but is updated to true after 3 seconds.

There's still an adjustment you may want to make even though that part appears to work ok. With isSubscriber in the dependency array, the function in your useEffect will be called any time isSubscriber changes. This probably not what you want, since this function doesn't actually depend on isSubscriber, but actually sets isSubscriber. In this case, that means test_equality() will be run on initial mount and then one more time after it sets isSubscriber, which is unnecessary.

This blog post explains the useEffect dependency array really well.

You can fix this by removing isSubscriber from the dependency array, like this:

  useEffect(() => {
    console.log("in useEffect");
    async function load_result() {
      const promise_function_return = await test_equality();
      setIsSubscriber(promise_function_return);
    }
    load_result();
  }, [isSubscriber]);

Since you mentioned the state value is not getting updated, there must be another issue going on in either get_customer_id() or customer_is_subscriber(). It would be good to double check and make sure the stripe api calls are working as expected.

Andrew Hulterstrom
  • 1,563
  • 4
  • 18