jineecode

인증번호 timer 본문

React native

인증번호 timer

지니코딩 2022. 5. 25. 16:06

timer를 구현하기 위해서는 useRef를 써야 한다. useState만으로는 closure 안의 값까지 업데이트 해줄 수 없기 때문이다.

https://ko.reactjs.org/docs/hooks-reference.html#useref

 

조건: 

1. 5분 이내에 인증번호 6자리 입력

const VALIDTIME = 300;
const time = useRef<number>(VALIDTIME);
// 이제 time.current 로 시간이 흘러가는 것을 추적할 수 있다.

let intervalRef: {current: NodeJS.Timeout | null} = useRef(null);

const [min, setMin] = useState(5);
const [sec, setSec] = useState(0);

2. 한 번 인증 버튼을 누르면, 20초 동안 인증 버튼을 누를 수 없음

3. 재시도 할 수 있는 횟수는 6번

  const VALIDCOUNT = 6;

1. 전화번호 유효성이 통과되면, '인증' 버튼 활성화 (redux dispatch)

2. 인증 버튼 onPress, '인증' 버튼 비활성화 (redux dispatch)

 2-1. timerReset callback

(처음엔 reset해주지 않아도 되지만 혹시 모르는 버그가 있을 수 있으므로 그냥 처음부터 5:00분으로 되돌리는 작업을 해주었음.)

  const timerReset = () => {
    clearInterval(intervalRef.current as NodeJS.Timeout);
    time.current = VALIDTIME;
    setMin(Math.floor(VALIDTIME / 60));
    setSec(VALIDTIME % 60);
  };

 2-2. DB에 등록된 전화번호가 있는지 API 호출 callback

 2-3. 2-2번에서 success를 타면, onPressTimer 호출 (물론 비동기로 문자 API도 같이 호출되어야 함)

 2-4. onPressTimer 콜백함수가 호출되면 순차적으로 실행됩니다.

const onPressTimer = () => {
    intervalRef.current = setInterval(decreaseNum, 1000);
    return () => clearInterval(intervalRef.current as NodeJS.Timeout);
  };

  useEffect(() => {
    return () => clearInterval(intervalRef.current as NodeJS.Timeout);
  }, []);

  useFocusEffect(
  // useEffect return 으로는 cleanup이 제대로 되지 않아서, useFocusEffect 로 한 번 더 제대로 cleanup 시켜줌.
    React.useCallback(() => {
      return () => {
        clearInterval(intervalRef.current as NodeJS.Timeout);
      };
    }, []),
  );

  const decreaseNum = () => {
    time.current -= 1; // 1초씩 감소

    setMin(Math.floor(time.current / 60)); //useState로 분, 시를 계속 업데이트 쳐준다
    setSec(time.current % 60);
  };

  useEffect(() => {
    if (time.current === VALIDTIME - 20 && certificationCounter > 0) {
    // 20초가 지나면 다시 인증버튼 활성화
      dispatch(setActiveCertification());
    }

    if (time.current <= 0) {
    // 시간이 초과되면 clearInterval 해줌
      clearInterval(intervalRef.current as NodeJS.Timeout);
    }
  }, [sec, certificationCounter, dispatch]);

 

input component의 UI

 <Input
            textContentType="oneTimeCode" // IOS 용
            value={sms}
            labelText="인증번호"
            placeholder="인증번호 6자리를 입력해주세요"
            onChangeText={(text) => checkCertNum(text)}
            keyboardType="numeric"
            maxLength={6}
            innerTextIsNumber={time.current === 0} //boolean
            innerText={
              time.current === 0
                ? '시간 초과'
                : `${min < 10 ? `0${min}` : min}:${sec < 10 ? `0${sec}` : sec}`
            }
/>

 

보너스. 문자 받으면 자동으로 input에 넣어주기 (단, 안드로이드만 가능하며 권한 승인을 받아야 함)

import {useSmsUserConsent} from '@eabdullazyanov/react-native-sms-user-consent';

...

const Test = () => {
...

 const retrievedCode = useSmsUserConsent();
      
 useEffect(() => {
    if (retrievedCode) {
      setSms(retrievedCode);
      setIsValidCertNum(true);
    }
 }, [retrievedCode]);

...
}

 

Comments