Skip to content

RFC add promise based method to obtain the token #13

@troZee

Description

@troZee

Hello,
Thank you for this library 🥇 I would like to propose another interesting feature to this library.

Description

Image the case, when the library is in invisible state and I would like to display the captcha, once user clicked on submit button in the form. When the submission is completed, then we can make an api call to apply the changes. So far I need to handle it using the callback, which is not the cleanest way to do it. Hence I would like to improve it using async/await handle

Posible implementation

Create a ref, which will be responsible for storing the promise

    const $promiseToken = useRef<{
      resolve: (value: GetToken | PromiseLike<GetToken>) => void;
      reject: (arg: any) => void;
    } | null>();

Move code to the separate method. It will be used in both places

    const onOpen = useCallback(() => {
      setVisible(true);
      setLoading(true);
      $isClosed.current = false;
    }, []);

Add new async method getToken, which will be responsible for opening the modal and fetching the token
This promise will be stored in the ref called $promiseToken

    useImperativeHandle(
      ref,
      () => ({
        open: () => {
          onOpen();
        },
        close: handleClose,
        getToken: () => {
          onOpen();
          return new Promise<GetToken>(
            (resolve, reject: (arg: any) => void) => {
              $promiseToken.current = { resolve, reject };
            }
          );
        },
      }),
      [handleClose, onOpen]
    );

Handle this promise in handleMessage method

    const handleMessage = useCallback(
   (content) => {
     try {
       const payload = JSON.parse(content.nativeEvent.data);
       if (payload.close) {
         if (isInvisibleSize) {
           handleClose();
         }
       }
       if (payload.load) {
         handleLoad(...payload.load);
       }
       if (payload.expire) {
         //@ts-ignore
         onExpire?.(...payload.expire);
       }
       if (payload.error) {
         handleClose();
         //@ts-ignore
         onError?.(...payload.error);
         $promiseToken?.current?.reject(payload.error);
       }
       if (payload.verify) {
         handleClose();
         //@ts-ignore
         onVerify?.(...payload.verify);
         const token = payload?.verify?.[0] || '';
         $promiseToken?.current?.resolve({ token: token });
       }
     } catch (err) {
       $promiseToken?.current?.reject(err);
     }
   },
   [onVerify, onExpire, onError, handleClose, handleLoad, isInvisibleSize]
 );

After everything, we need to clean up a little bit to avoid memory leak

    React.useEffect(() => {
      return () => {
        $promiseToken.current = null;
      };
    }, []);

Before

const App = () => {
  const size = 'normal';
  const [key, setKey] = useState('<none>');

  const $recaptcha = useRef();

  const handleOpenPress = useCallback(() => {
    $recaptcha.current.open();
  }, []);

  const handleClosePress = useCallback(() => {
    $recaptcha.current.close();
  }, []);


  useState(() => {
   // handle submit here (api call), which is not the best solution. I would prefer to have it all at one place
  },[key])

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={styles.safeArea}>
        <View
          contentInsetAdjustmentBehavior="automatic"
          style={styles.container}>
          <Button onPress={handleOpenPress} title="Open" />
          <Text>Token: {key}</Text>
          <Text>Size: {size}</Text>
        </View>

        <Recaptcha
          ref={$recaptcha}
          lang="en"
          headerComponent={<Button title="Close" onPress={handleClosePress} />}
          siteKey="6LejsqwZAAAAAGsmSDWH5g09dOyNoGMcanBllKPF"
          baseUrl="http://127.0.0.1"
          size={size}
          theme="light"
          onLoad={() => alert('onLoad event')}
          onClose={() => alert('onClose event')}
          onError={(err) => {
            alert('onError event');
            console.warn(err);
          }}
          onExpire={() => alert('onExpire event')}
          onVerify={(token) => {
            alert('onVerify event');
            setKey(token);
          }}
        />
      </SafeAreaView>
    </>
  );
};

After

const App = () => {
  const size = 'normal';
  const [key, setKey] = useState('<none>');

  const $recaptcha = useRef();

  const handleOpenPress = useCallback(() => {
    const response = await $recaptcha?.current?.getToken();
    if (response?.token) {
    // do some api calls etc 
    } else {
    // do some api calls etc 
      Alert.alert('Title', 'Something went wrong');
    }  }, []);

  const handleClosePress = useCallback(() => {
    $recaptcha.current.close();
  }, []);

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={styles.safeArea}>
        <View
          contentInsetAdjustmentBehavior="automatic"
          style={styles.container}>
          <Button onPress={handleOpenPress} title="Open" />
          <Text>Token: {key}</Text>
          <Text>Size: {size}</Text>
        </View>

        <Recaptcha
          ref={$recaptcha}
          lang="en"
          headerComponent={<Button title="Close" onPress={handleClosePress} />}
          siteKey="6LejsqwZAAAAAGsmSDWH5g09dOyNoGMcanBllKPF"
          baseUrl="http://127.0.0.1"
          size={size}
          theme="light"
          onLoad={() => alert('onLoad event')}
          onClose={() => alert('onClose event')}
          onError={(err) => {
            alert('onError event');
            console.warn(err);
          }}
          onExpire={() => alert('onExpire event')}
          onVerify={(token) => {
            alert('onVerify event');
            setKey(token);
          }}
        />
      </SafeAreaView>
    </>
  );
};

WDYT about above approach ? Feel free to comment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions