Search code examples
reactjsreact-hooksplaidusecallback

onSuccess callback in Plaid Link not updating


I've built a PlaidLink component using react-plaid-link as below. There's no issues when building with the standard way - passing in only public_token and account_id to the request body.

However, when I attempt to pass in stripeUid to the request body, only an empty string (the initial value of the stripeUid state) is passed. This is despite the value of stripeUid being updated and passed in correctly from the parent via props. For some reason stripeUid does not update within the useCallback hook even though the value is in the dependency array.

Any idea why the value is not updating?

function PlaidLink(props) {
  const [token, setToken] = useState("");
  const { achPayments, stripeUid } = props;

  async function createLinkToken() {
    const fetchConfig = {
      method: "POST",
    };
    const response = await fetch(
      API_URL + "/plaid/create-link-token",
      fetchConfig
    );
    const jsonResponse = await response.json();
    const { link_token } = jsonResponse;
    setToken(link_token);
  }

  const onSuccess = useCallback(
    (publicToken, metadata) => {
      const { account_id } = metadata;

      // Exchange a public token for an access one.
      async function exchangeTokens() {
        const fetchConfig = {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            public_token: publicToken,
            account_id,
            stripeUid,
          }),
        };
        const response = await fetch(
          API_URL + "/plaid/exchange-tokens",
          fetchConfig
        );
        const jsonResponse = await response.json();
        console.log("Exchange token response:", jsonResponse);
      }

      exchangeTokens();
    }, [stripeUid]
  );

  const { open, ready } = usePlaidLink({
    token,
    onSuccess,
  });

  // get link_token from your server when component mounts
  useEffect(() => {
    createLinkToken();
  }, []);

  useEffect(() => {
    if (achPayments && ready) {
      open();
    }
  }, [achPayments, ready, open]);

  return <div></div>;
}

export default PlaidLink;

Solution

  • I am not familiar with Stripe API but from reading a code I see a possible issue with the code.

    Following the chain of events, there is one usePlaidLink and two useEffects. When the component mounts, it createLinkToken in one of the effects and open in the other (assuming it is ready).

    However, when stripeUid changes, it doesn't re-fire the effects. So, that's a hint for me.

    Next, checking the source of usePlaidLink here: https://github.com/plaid/react-plaid-link/blob/master/src/usePlaidLink.ts gives me an idea: it doesn't do anything when options.onSuccess changes, only when options.token changes. This is their dependency array:

    [loading, error, options.token, products]
    

    So it looks like your code is correct as far as effects in react go, but it doesnt't work together because changing the onSuccess doesn't do anything.

    How to solve:

    • make a pull request into the open source library to fix the issue there
    • inline that library into your code and fix it for yourself
    • use "keyed components" to unmount and mount the component again when the uid changes instead of updating to work around the issue
    • some other solution