Получение странного поведения в объекте useRef() Компонент React-Native

Я работаю с функцией автозаполнения OTP. Я использую react-native-otp-textinput, который работает. отлично. Я использую useRef() для текстового поля OTP, поэтому, когда я получу SMS, я буду использовать это useRef() и заполнять его значением.

Постановка проблемы:

  1. Когда я запускаю приложение в первый раз, мой useRef() ведет себя нормально, и я могу автоматически заполнить элемент userRef().current.setValue().
  2. Когда я запускаю приложение во второй или третий раз, я всегда получаю эту ошибку

TypeError: Невозможно прочитать свойство setValue null, js engine: hermes

То, что я сделал, было захватить, что было причиной этого. Поэтому я утешил это в своей функции. Я обнаружил в первый раз, что console.log(useRef().current) печатает данные, но когда дело доходит до второго или третьего раза, он возвращает null. Я в замешательстве, почему это происходит?

Вот мой код:

 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]);

Я полностью запутался в этом, попытался выполнить следующее: ">TypeError: Невозможно прочитать свойство setValue нулевого JavaScript.

Редактировать

Вот как мой OTP TextInput выглядит в 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)}
            />

введите здесь описание изображения


person Alok    schedule 13.04.2021    source источник
comment
Я никогда не использовал метод setValue из useRef и не могу найти упоминания о нем в документации. Где ты это увидел?   -  person Konstantin    schedule 13.04.2021
comment
@Konstantin setValue — метод ввода текста OTP (см. ссылку в вопросе)   -  person yuvin    schedule 13.04.2021
comment
Не могли бы вы поделиться частью рендеринга вашего компонента, как именно вы определяете OTPTextInput в JSX?   -  person yuvin    schedule 13.04.2021
comment
Привет, @yuvin, я добавил код в раздел EDIT. Дайте мне знать, если вам нужно что-то еще с моей стороны.   -  person Alok    schedule 13.04.2021
comment
Это, вероятно, не решит эту конкретную проблему, но в целом, когда вы получаете доступ к ссылке, делайте ref.current?. вместо ref?.current (сама ссылка всегда определена, но реквизит current может и не быть).   -  person Marek Lisik    schedule 13.04.2021
comment
Что, если вы попытаетесь заменить ref={otpInputRef} ссылкой обратного вызова, как это предлагается в примере с компонентом: ref={e => (otpInputRef = e)}? Просто идея...   -  person yuvin    schedule 13.04.2021
comment
Пробовал уже @yuvin выдает ошибку, мол TypeError: "otpInputRef" is read-only   -  person Alok    schedule 13.04.2021
comment
Хм... Может быть, это должно быть let, тогда вместо этого, если const. Этот компонент вообще работает? :)   -  person yuvin    schedule 13.04.2021
comment
Вы упомянули, что оно работает в первый раз, но затем вы запускаете приложение во второй раз, и оно больше не работает. Что именно происходит между ними? Вы закрываете приложение? Или вы просто ждете, пока придет следующее SMS?   -  person yuvin    schedule 13.04.2021
comment
Да, это работает @yuvin в первый раз. Итак, во второй раз я просто logout и снова login. OTP просто 2FA в моей системе.   -  person Alok    schedule 13.04.2021
comment
Итак, я предполагаю, что ваш компонент OTP (который содержит OTPTextInput) перезагружается (размонтируется/подключается), когда вы выходите из системы и снова входите в систему? Я пытаюсь создать пример кода для воспроизведения этого   -  person yuvin    schedule 13.04.2021
comment
@yuvin ты спас мне жизнь в этой let части. Изменение const на let для моего otpInputRef сработало для меня. Поскольку он был частью useRef, из-за этого он не обновлялся сразу. Теперь у нас есть let, он вносит изменения, как только мы назначаем его <TextInput> :D. Вы спаситель. Пожалуйста, напишите ответ, и я проголосую за него или дайте мне знать, я напишу ответ вкратце для наших будущих разработчиков :D   -  person Alok    schedule 13.04.2021
comment
Рад, что это сработало! У меня никогда не было возможности поближе познакомиться с React Native, так что это был хороший шанс для меня :) Я использовал Snack и мне нравится, как он легко интегрируется и работает на моем устройстве. Я добавлю ответ, не стесняйтесь голосовать за него или редактировать, если это необходимо   -  person yuvin    schedule 13.04.2021
comment
Сделайте это и дайте мне знать @yuvin, я отредактирую и поддержу ваш ответ. Будет ждать этого. Ты заслуживаешь плюса, мой друг :D   -  person Alok    schedule 13.04.2021


Ответы (1)


const не следует использовать в качестве типа данных для ref, он должен быть let. Причина в соответствии с жизненным циклом React-Native:

Сначала просмотрите загрузки, а затем обновите useEffect, поэтому const просто инициализировал значение ref только как нулевое. пусть разрешит значение ref useRef() для обновления и упростит работу

 // 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]);      
person yuvin    schedule 13.04.2021
comment
Спасибо Ювин за ответ, я улучшил ответ. Спасибо за ваше предложение :) - person Alok; 13.04.2021