Scenario


ReactNative로 타이머를 만들고 있는데,

Props에 따라 state를 변화시켜야 할때, componentWillReceiveProps(newtProps)를 써야할때가 있다.

*(이 메소드 안에서 this.setState()를 해도 추가적으로 렌더링하지 않는다.)


JavaScript 0.44 KB
  1.     componentWillReceiveProps (nextProps) {
  2.         const currentProps = this.props;
  3.         if(!currentProps.isPlaying && nextProps.isPlaying) {
  4.             const timerInterval = setInterval(() => {currentProps.addSecond();}, 1000);
  5.             this.setState({
  6.                 timerInterval
  7.             })
  8.         } else if(currentProps.isPlaying && !nextProps.isPlaying) {
  9.             clearInterval(this.state.timerInterval);
  10.         }
  11.     }


위 코드가 타이머에서 elapsedTime(지금까지 카운팅한 시간) state를 증가시키는 로직인데,


if(!currentProps.isPlaying && nextProps.isPlaying)

이런식으로 조건을 주는게 직관적이지도 않고 왠지 비효율적으로 보였다.


이 소스를 단지 

if(nextProps.isPlaying

이런식의 조건식으로 바꿀수 없는것일까? 생각해보았다.


Solution


결론부터 말하자면 componentWillReceiveProps(newtProps) 에서 setState를 해야할 경우에는 무조건 이전값과 다음값을 비교한뒤에 해야한다.


현재 props와 next props를 비교하지 않고 setState를 하면 어떻게 되는지 알아보기위해


  1.         if(currentProps.isPlaying) {
  2.             const timerInterval = setInterval(() => {currentProps.addSecond();}, 1000);
  3.             this.setState({
  4.                 timerInterval
  5.             })
  6.         }

이런식으로 소스코드를 바꿨더니, setInterval 함수가 여러번 실행되서 카운팅 속도가 1초주기가 아닌, 점점 빨라졌다.



(카운트가 빨라진 앱의 모습)



왜 그럴까?



그림 출처: https://velopert.com/1130 - velopert님


일단 Reactjs Life Cycle 부터 살펴보자면 위와 같다.


만약 Prop의 변화를 감지해서 State를 변환시켜 줄때는 componentWillReceiveProps에서 해주는게 맞다.


왜냐하면 shouldComponentUpdate 이후에는 setState를 할 수 없다.


--------------------------------------------------------

본론으로 들어가서 왜 그럴까?


Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.

출처:https://reactjs.org/docs/react-component.html


리액트 공식문서를 살펴보면 componentWillReceiveProps에 대해 위와같이 서술하고 있다.

"이 메소드는 props가 변하지 않더라도 Call 할 수 있으므로, 현재 current value와 next value를 꼭 비교해서 써야하며, 이는 상위 component가 너의 컴포넌트를 re-render 시킨다" 고 설명한다


더 이해하기 쉽게 예를들어보자,


예를들어, 타이머가 현재 카운팅을 하고있어서 현재 isPlaying 값이 true라고 가정해보자.

현재 isPlaying이 true 면서 props가 바뀌지 않았을때 componentWillReceiveProps 메소드를 콜 한다면, if(nextProps.isPlaying) 의 조건식은 항상 참 일수 밖에없다.


그러나 if(!currentProps.isPlaying && nextProps.isPlaying) 형식으로 조건문을 걸어준다면, false && true 이므로, props가 변동이 있을때만, 이 조건문이 실행되게 되는 것 이다.


 

 current.isPlaying

nextProps.isPlaying

when props are not Changed

 true

 true

when props are Changed

 true

 false 



Scenario


개인적으로 리액트 네이티브를 공부하다가, Expo 서버 연결 없이 안드로이드 앱을 공기계에서 실행해보고싶었다.

예전 앱개발을 할땐 apk파일이 자동적으로 떨궈졌는데

ReactNative로 apk파일을 얻어보려니, 공식 DOC에도 제공을 하지 않아 이것저것 삽질(?) 을 시작해봤다.



Solution


One of the points of Expo on top of React Native is that you don't go down to android or ios code. Expo deals with those folders for you, you don't need to interact with them. Is there a reason you need those folders? if so, you will have to detach. Here's the documentation to do so


설명: Expo를 React-Native에서 공식적으로 패키징 시키면서, android나 ios 코드로 직접 접근하지 않게 해놨다.

(근데 듀토리얼은 왜 예전 기준일까?)

출처:https://stackoverflow.com/questions/44270504/react-native-ios-and-android-folders-not-present


삽질 좀 하다가 프로젝트 구조를 예전으로 되돌릴 방법을 찾아 냈다.


다음 세가지 순서로 진행된다.

1.expo 에서 expo 프레임워크를 제거 한다.

npm run eject

2.Bundle debug build 를한다 

react-native bundle --dev false --platform android --entry-file index.android.js --bundle-output ./android/app/build/intermediates/assets/debug/index.android.bundle --assets-dest ./android/app/build/intermediates/res/merged/debug

3.Create debug build를 한다

cd android
./gradlew assembleDebug

For Windowsgradlew assembleRelease

Generated apk will be located at android/app/build/outputs/apk


출처:https://stackoverflow.com/questions/35283959/build-and-install-unsigned-apk-on-device-without-the-development-server


Scenario


원래는 Expo를 이용해서 React Native 앱을 핸드폰에서 실행해서 디버깅을 했다.

그런데 방화벽 문제가 생기면서 Expo로 핸드폰에서 앱을 실행 할 수 없었다.

Expo로 App을 Create 하지않고 React Native로 공식 문서를 보며 안드로이드 AVD로 개발환경을 구축하고자 했다.

그렇게 나의 삽질은 시작되었다..



Solution


1. npm install -g create-react-native-app

 일단 RN 앱을 쉽게 만들기 위해 create-react-native-app을 설치한다


2.create-react-native-app AwesomeProject && cd AwesomeProject && npm start

여기까지면 Rn 프로젝트를 만든 것이다.


3. Android Studio 와 JDK를 설치해야한다. (JDK는 환경변수도 설정, 이건 패스)


4.안드로이드 스튜디오를 키고 Tools -> android -> AVD manager 메뉴에 들어가서 nexus 5를 설치한다

 참고

 -Tools -> Android 메뉴조차 활성화가 안돼있을때가 있다. 그럴때는

 4.1.우측 하단에 Event Log 를 누른다

 4.2.클릭하면 에러메세지가 뜨는데 그것을 클릭하면 알아서 인스톨을 해준다 (이거때문에 거의 3시간 삽질함)

 출처: https://stackoverflow.com/questions/46948322/how-to-open-avd-manager-in-android-studio-3-0-version/47143861

 


5.이제 AVD를 켠뒤에 아까 만든 폴더에 들어가서 npm run android 라고 치면 실행이 된다.

만약 안될경우에는 재부팅을 한번 해보자(여기서 1시간 정도 삽질함ㄴㅇ라ㅓㄴ)


6.Ctrl + M 으로 핫로딩 설정도 완료 할 수있다.

(Hot loading도 아주 잘되는걸 볼 수 있다.)

+ Recent posts