물에 살고싶은 개발자

[react-navigation] Cannot read property 'navigation' of undefined 해결법 본문

React-Native

[react-navigation] Cannot read property 'navigation' of undefined 해결법

돼지사랑 2019. 6. 20. 16:46

단도직입적으로 바로 시작 ! 그리고 결론 ! 

RN의 라이프사이클에 대해 이해하고 사용하자 !

생성자에서 this.props.navigation.navigate("open")을 사용해서 해당 에러가 발생한 경우, 

생성자 말고 componentDidMount() 를 이용하면 된다. 

즉, props가 세팅 된 후에 사용하면 된다! 


뜬금없는 추가 팁. 생성자엔 super(); 가 꼭 있어야한다. 없으면 무슨일이 발생하는지 궁금하면 생성자를 정의해놓고 직접 확인해보시라. 


Cannot read property 'navigation' of undefined 이라는 에러가 발생하는 경우가 있다.

내 경우에는 App.js에 사용할 화면들을 쭉 정의해놓고 시작하자마자 스플래시 화면으로 보내려고 했다.

자연스레 생성자에서 this.props.navigation.navigate("open") 코드를 실행했는데, 

Cannot read property 'navigation' of undefined 해당 에러와 함께 화면이동이 되지 않았다.

이리저리 검색을 해보다가 이전에 테스트 프로젝트에서는 됏는데 왜 안돼지? 라는 의문이 들어 확인해봣는데


테스트 프로젝트에선 아래와 같이 await를 사용해서 화면이동을 시키고 있었다. 

_goto = async () => {
const userToken = await AsyncStorage.getItem("@userToken");
const isOpen = await AsyncStorage.getItem("@isOpen");

console.log("ㅇㅋ 호출ㄱ " + userToken);
if (userToken != null) {
console.log("로그인되어있음 " + isOpen);
this.props.navigation.navigate("isMain");
} else {
console.log("로그인안됨 " + isOpen);
if (isOpen != null) {
this.props.navigation.navigate("login");
} else {
this.props.navigation.navigate("isOpening");
}
}
};


_goto() 는 생성자에서 호출했고, 나머지는 모두 동일했다. 

왜 이런문제가 생겼을까 하고 잘 고민해보니 await는 뒤에있는 명령어를 실행할때까지 기다리라는 코드였던게 생각났고, 

혹시 생성자에선 props가 정의되지 않았나? 그래서 그러나!? 라는 생각이 들어 지금 프로젝트에도 똑같이 유저토큰을 꺼내오는 코드인 

const userToken = await AsyncStorage.getItem("@userToken");

를 정의해서 사용해봤더니 정상적으로 실행되는걸 확인할 수 있었다. 


그래서 "아 이거 props나 state는 라이프사이클에 영향을 받나보네" 라는 가설을 세우게 되었고, 일단 라이프사이클에 대해 검색해봤다.

나의 경우는 이 블로그를 보고 내용을 대충 파악했지만, 알기쉽게 보자면 아래 이미지와 같다.

props에 대한 공부를 설렁설렁해서 어느 타이밍에 props.navigation이 세팅되는지는 모르겠지만, 

리액트 네비게이션 공식홈페이지에 있는 가이드를 보면, render 안에 View를 정의하는 부분에서 호출하면 정상적으로 기능하는걸 확인할 수 있다. 


그러므로 [생성자에선 네비게이션이 세팅되지 않았다.] -> [근데 공식가이드에선 View에서 사용하니 된다] -> [그러므로 그 이후에 사용하면 될것이다] 라는 기적의 3단논법이 완성되었다.


처음엔 그냥 테스트프로젝트처럼 생성자에서 호출한 Method에 await를 걸면 되겠지...했는데 생각해보니까 안일한 생각이었던게, 스레드라는게 항상 내가 의도한대로 동작하지 않기때문에 만약의 경우 await가 render보다 먼저 끝나버리면 오류코드가 되어버릴 가능성이 있다는 것이다. (물론 이 가능성도 내가 RN에 대해 깊게 공부하지 않았기때문이다. 좀 덜 바빠지면 각잡고 생명주기를 제대로 파보던지 해야지 원..)

그래서 생명주기가 다 끝난 뒤 호출되는 componentDidMount() 에서 실행해보니 아주 깔~끔하게 원하는대로 실행되는걸 확인 할 수 있었다.


그러므로 결론은 맨 위에 미리 써둔것처럼, 생명주기 관련해서 아직 세팅이 되지 않은걸 호출할때 생기는 오류메시지이므로 타이밍을 늦춰서 실행하면 네비게이션이 세팅된 후에 해당코드를 실행하게 되므로 깔~끔하게 실행된다는 것이다. 


깔~끔하게 성공한 코드를 아래에 공유하고 글을 마치겠다.



..위는 각종 import문..
class App extends React.Component {
componentDidMount(){
console.log("컴포넌트 디드 마운트!");
this.props.navigation.navigate("open"); //스플래시화면으로 이동
}

render() {
return (
<View>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
..아래는 네비게이션 화면정의..


깔~끔하게 여기서 끝

Comments