Search code examples
javascriptreact-nativereact-hooksuse-ref

Getting weird behavior in useRef() object React-Native component


I am working with the OTP auto-fill feature. I am using react-native-otp-textinput, which is working fine. I am using useRef() for the OTP Textfield, so when I receive a SMS, I will use this useRef(), and fill it in the value.

Problem Statement:

  1. When I run the app for the first time, my useRef() behaves fine, and I can auto fill the item with userRef().current.setValue().
  2. When I run the app for the second or third time, I always get this error

TypeError: Cannot read property 'setValue' of null, js engine: hermes

What I did was to capture, what was the reason behind it. So I did console it out in my function. I found out for the first time, console.log(useRef().current) prints the data, but when it comes to second or third time, it returns null. I am confused, why is this happening?

Here is my code:

 const otpInputRef = useRef(null);

 const startReadSMSListerner = async () => {
   // Checking permission, else getting one
   const hasPermission = await ReadSms.requestReadSMSPermission();
   // If given permission, start listening, else, leave it
   if(hasPermission){
     ReadSms.startReadSMS((status, sms, error) => {
       if(status === 'success'){
         console.log(otpInputRef.current); // <-- Comes fine for the first time, but null when we test it in the second time
         otpInputRef?.current.setValue(sms); // <--- Here is the code which is working weird
       }
     });
   }
 }
 
 useEffect(() => {
  if(Platform.OS === 'android') startReadSMSListerner();

  return () => ReadSms.stopReadSMS();
 }, [otpInputRef]);

I am totally confused on this, tried following this up: TypeError: Cannot read property of setValue of null JavaScript.

Edit

Here is how my OTP TextInput looks in JSX

            <OTPTextInput
              ref={otpInputRef}
              defaultValue=""
              inputCount={4}
              keyboardType="numeric"
              returnKeyType="done"
              textContentType="oneTimeCode" // for iOS Autofill enable
              containerStyle={{justifyContent: 'center', padding: width / 15}}
              textInputStyle={styles.textInputContainer}
              handleTextChange={(text) => onChangetText(text)}
            />

enter image description here


Solution

  • const should not be used as a datatype for the ref, it should be let. Reason being as per the lifecycle of React-Native:

    View loads first, and useEffect updates afterwards, so const was just initialising ref value as null only. let enabled the ref useRef() value to update, and make the job easier

     // This does the magic
     let otpInputRef = useRef(null);
    
     const startReadSMSListerner = async () => {
       // Checking permission, else getting one
       const hasPermission = await ReadSms.requestReadSMSPermission();
       // If given permission, start listening, else, leave it
       if(hasPermission){
         ReadSms.startReadSMS((status, sms, error) => {
           if(status === 'success'){
             otpInputRef.current?.setValue(sms); // <--- Works now, since it gets updated in the useEffect() call
           }
         });
       }
     } 
    
     useEffect(() => {
      if(Platform.OS === 'android') startReadSMSListerner();
    
      return () => ReadSms.stopReadSMS();
     }, [otpInputRef]);