useNavigation
Screen으로 사용 중인 컴포넌트에서는 Props를 통해 navigation 또는 route 객체를 사용할 수 있지만, Screen으로 사용되지 않는 다른 컴포넌트에서는 navigation 또는 route를 Props로 받아와서 사용할 수 없다.
예를 들어 HomeScreen에서 상세보기 버튼을 DetailViewButton 이라는 컴포넌트로 따로 분리해서 사용하고 싶다면 다음 세 가지 방법을 통해 구현할 수 있다.
1. SendMessageButton 컴포넌트에 onPress 함수를 전달
function DetailViewButton({onPress}) {
return <Button title="상세보기" onPress={onPress} />;
}
function HomeScreen({navigation}) {
return (
<View>
<Text>Home</Text>
<DetailViewButton onPress={() => navigation.push('Detail', {id: 1})} />
</View>
);
}
2. SendMessageButton 컴포넌트에 navigation 객체를 바로 전달
function DetailViewButton({navigation}) {
return (
<Button
title="상세보기"
onPress={() => navigation.push('Detail', {id: 1})}
/>
);
}
function HomeScreen({navigation}) {
return (
<View>
<Text>Home</Text>
<DetailViewButton navigation={navigation} />
</View>
);
}
3. useNavigation Hook 사용
import {useNavigation} from '@react-navigation/native';
...
function DetailViewButton() {
const navigation = useNavigation();
return (
<Button
title="상세보기"
onPress={() => navigation.push('Detail', {id: 1})}
/>
);
}
function HomeScreen() {
return (
<View>
<Text>Home</Text>
<DetailViewButton />
</View>
);
}
...
useNavigation Hook을 사용하면 Screen으로 사용되고 있지 않은 컴포넌트에도 navigation 객체를 사용할 수 있다.
useRoute
useRoute는 useNavigation과 비슷하게, Screen이 아닌 컴포넌트에서 route 객체를 사용할 수 있도록 해준다.
import {useRoute} from '@react-navigation/native';
...
function IdText() {
const route = useRoute();
return <Text>id: {route.params.id}</Text>;
}
function DetailScreen({navigation, route}) {
return (
<View>
<IdText />
</View>
);
}
...
useFocusEffect
useFocusEffect는 화면에 포커스가 잡혔을 때 특정 작업을 할 수 있게 하는 Hook다.
만약 HomeScreen에서 DetailScreen을 띄운다면 HomeScreen이 화면에서 사라지는게 아니라, HomeScreen 위에 DetailScreen을 쌓아서 보여주는 것이다. 그래서 useEffect Hook을 통해서 컴포넌트가 마운트되거나 언마운트될 때 콘솔에 텍스트를 출력한다면 DetailScreen을 띄울 때 컴포넌트가 언마운트 되지 않고, 또 뒤로가기하여 HomeScreen으로 돌아왔을 때 컴포넌트가 마운트되지 않는 것을 확인할 수 있다.
function HomeScreen() {
useEffect(() => {
console.log('mounted');
return () => {
console.log('unmounted');
};
}, []);
return (
<View>
<Text>Home</Text>
<DetailViewButton />
</View>
);
}
useEffect를 사용해 마운트와 언마운트 시 콘솔을 찍도록 하였다.
시뮬레이터에서 리로딩해보면 mounted만 찍히는것을 확인할 수 있다.
DetailViewButton을 클릭 해 화면을 이동하고, 뒤로가기 시에도 unmounted가 찍히지 않는다.
만약 다른 화면을 열었다가 돌아왔을 때 특정 작업을 하고 싶다면 useFocusEffect Hook을 사용해야 한다. 또 현재 화면에서 다른 화면으로 넘어갈 때 특정 작업을 하고 싶다면 useFocusEffect에서 함수를 만들어 반환하면 된다.
useFocusEffect는 꼭 useCallback과 같이 사용해야 한다. 만약 useFocusEffect을 사용하지 않으면 컴포넌트가 리렌더링 될 때마다 useFocusEffect에 등록한 함수가 호출될 것이다.
useCallback은 컴포넌트 내부에서 함수를 만들 때, 새로 만든 함수를 사용하지 않고 이전에 만든 함수를 다시 사용하도록 만들어준다. 그리고 그 함수 내부의 로직에서 의존하는 값이 있다면 의존하는 값이 바뀌었을 때 함수를 교체할 수 있도록 해준다.
import React, {useCallback} from 'react';
import {useFocusEffect} from '@react-navigation/native';
...
function HomeScreen() {
useFocusEffect(
useCallback(() => {
console.log('이 화면을 보고 있어요.');
return () => {
console.log('다른 화면으로 넘어갔어요.');
};
}, []),
);
return (
<View>
<Text>Home</Text>
<DetailViewButton />
</View>
);
}
...
이제 다시 DetailViewButton을 클릭 해 화면을 이동하고, 뒤로가기를 눌러보면 로그가 정상적으로 찍히는 것을 확인할 수 있다.