Toast message는 아래 예시처럼 짧은 시간 나타났다 사라지는 메시지를 말합니다.
Toast message는 Android에서 기본으로 지원하는 기능으로 React Native의 공식 문서에도 Android 한정으로 지원을 해주고 있습니다.
https://reactnative.dev/docs/toastandroid
이번 게시글에서는 javascript로 Toast 메시지를 구현하는 방법을 정리하려 합니다.
필요 라이브러리
* react-native-reanimated v2 설치가 필요하신 분은 아래 링크를 참고해도 좋을 거 같아요😊
https://coding-w00se.tistory.com/39
진행 순서
1. Toast 기능 구현
step 0. View 생성
기능 구현을 위해 생성한 파일은 총 두 개입니다.
- ToastTestScreen.js: Toast 메시지를 테스트할 screen 파일
- Toast.js: Toast message 컴포넌트
ToastTestScreen.js
import React, { useCallback, useRef } from 'react';
import {
View,
StyleSheet,
Button
} from 'react-native';
import Toast from '../components/Toast';
function ToastTestScreen(props) {
const toastRef = useRef(null);
const onPress = useCallback(()=>{
toastRef.current.show("토스트 메시지~!");
}, []);
return (
<View style={styles.rootContainer}>
<Button title="토스트 팝업!" onPress={onPress}/>
<Toast ref={toastRef}/>
</View>
);
}
const styles = StyleSheet.create({
rootContainer: {
flex: 1,
backgroundColor: "#ffffff",
justifyContent: "center",
alignItems: "center",
}
});
export default ToastTestScreen
Toast.js
import React, { useRef, useCallback, forwardRef, useImperativeHandle } from 'react';
import {
View,
StyleSheet,
Text
} from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withSequence,
runOnJS,
} from 'react-native-reanimated';
const Toast = forwardRef((props, ref) => {
const toastOpacity = useSharedValue(0);
const isShowed = useRef(false);
const animatedStyle = useAnimatedStyle(()=>{
return {
opacity: toastOpacity.value,
}
}, []);
useImperativeHandle(ref, () => ({
show: show
}));
const show = useCallback((message) => {
}, []);
return (
<Animated.View style={[ styles.rootContainer, animatedStyle ]}>
<Text style={styles.message}>이것은 아마 토스트 메시지?</Text>
</Animated.View>
);
})
const styles = StyleSheet.create({
rootContainer: {
position: "absolute",
bottom: 100,
backgroundColor: "rgb(95, 209, 251)",
paddingVertical: 9,
paddingHorizontal: 23,
borderRadius: 100,
},
message: {
color: "rgb(255, 255, 255)"
}
});
export default Toast;
step 1. Toast 기능 구현
기능은 구현은 아래 두 가지 단계로 나뉩니다.
1) 메시지를 보이게 하는 이벤트 구현
2) 이벤트 호출에 따라 메시지의 투명도를 조절
1. 메시지를 보이게 하는 이벤트 구현
-> Toast.js의 부모 컴포넌트에서 Toast 메시지를 보이게 하는 함수에 접근할 수 있도록 해야 합니다.
해당 기능은 useImperativeHandle hook으로 할 수 있습니다.
*useImperativeHandle hook에 대한 설명은 공식 문서를 통해 확인할 수 있습니다.
https://ko.reactjs.org/docs/hooks-reference.html#useimperativehandle
2. 이벤트 호출에 따라 메시지의 투명도를 조절
-> 이벤트가 호출됐을 때 Toast 메시지의 opacity를 1로 만드는 애니메이션 실행, 애니메이션이 완료되면 다시 opacity를 0으로 만드는 애니메이션 실행 순으로 구현할 수 있습니다.
Toast 기능 구현 관련 코드는 아래와 같습니다.
- Toast.js
import React, { useState, useRef, useCallback, forwardRef, useImperativeHandle } from 'react';
import {
View,
StyleSheet,
Text
} from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withSequence,
runOnJS,
} from 'react-native-reanimated';
const Toast = forwardRef((props, ref) => {
const [ message, setMessage ] = useState("");
const toastOpacity = useSharedValue(0);
/**
* isShowed를 통해 애니메이션이 중복으로 실행되는 것을 방지
*/
const isShowed = useRef(false);
const animatedStyle = useAnimatedStyle(()=>{
return {
opacity: toastOpacity.value,
}
}, []);
/**
* useImperativeHandler를 통해 show 함수를 외부에서 접근할 수 있도록 허용
*/
useImperativeHandle(ref, () => ({
show: show
}));
const turnOnIsShow = useCallback(()=>{
isShowed.current = false;
}, []);
/**
* show 함수가 실행되면 toastOpacity를 변경하는 animation이 실행된다.
* 애니메이션은 opacity를 1로 만드는 애니메이션 -> opacity를 0으로 만드는 애니메이션 순으로 실행된다.
*/
const show = useCallback((message) => {
if (!isShowed.current) {
setMessage(message);
isShowed.current = true;
toastOpacity.value = withSequence(
withTiming(1, { duration: 2000 }),
withTiming(0, { duration: 2000 }, () => {
runOnJS(turnOnIsShow)();
}),
);
}
}, []);
return (
<Animated.View style={[ styles.rootContainer, animatedStyle ]}>
<Text style={styles.message}>{message}</Text>
</Animated.View>
);
})
const styles = StyleSheet.create({
rootContainer: {
position: "absolute",
bottom: 100,
backgroundColor: "rgb(95, 209, 251)",
paddingVertical: 9,
paddingHorizontal: 23,
borderRadius: 100,
},
message: {
color: "rgb(255, 255, 255)"
}
});
export default Toast;
여기까지 구현하면 아래와 같이 구현이 됩니다.
전체 코드
ToastTestScreen.js
import React, { useCallback, useRef } from 'react';
import {
View,
StyleSheet,
Button
} from 'react-native';
import Toast from '../components/Toast';
function ToastTestScreen(props) {
const toastRef = useRef(null);
const onPress = useCallback(()=>{
toastRef.current.show("토스트 메시지~!");
}, []);
return (
<View style={styles.rootContainer}>
<Button title="토스트 팝업!" onPress={onPress}/>
<Toast ref={toastRef}/>
</View>
);
}
const styles = StyleSheet.create({
rootContainer: {
flex: 1,
backgroundColor: "#ffffff",
justifyContent: "center",
alignItems: "center",
}
});
export default ToastTestScreen
Toast.js
import React, { useState, useRef, useCallback, forwardRef, useImperativeHandle } from 'react';
import {
View,
StyleSheet,
Text
} from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withSequence,
runOnJS,
} from 'react-native-reanimated';
const Toast = forwardRef((props, ref) => {
const [ message, setMessage ] = useState("");
const toastOpacity = useSharedValue(0);
const isShowed = useRef(false);
const animatedStyle = useAnimatedStyle(()=>{
return {
opacity: toastOpacity.value,
}
}, []);
useImperativeHandle(ref, () => ({
show: show
}));
const turnOnIsShow = useCallback(()=>{
isShowed.current = false;
}, []);
const show = useCallback((message) => {
if (!isShowed.current) {
setMessage(message);
isShowed.current = true;
toastOpacity.value = withSequence(
withTiming(1, { duration: 2000 }),
withTiming(0, { duration: 2000 }, () => {
runOnJS(turnOnIsShow)();
}),
);
}
}, []);
return (
<Animated.View style={[ styles.rootContainer, animatedStyle ]}>
<Text style={styles.message}>{message}</Text>
</Animated.View>
);
})
const styles = StyleSheet.create({
rootContainer: {
position: "absolute",
bottom: 100,
backgroundColor: "rgb(95, 209, 251)",
paddingVertical: 9,
paddingHorizontal: 23,
borderRadius: 100,
},
message: {
color: "rgb(255, 255, 255)"
}
});
export default Toast;
읽어 주셔서 감사합니다 :)
잘못된 부분이 있다면 댓글로 편히 알려주세요😊
'Frontend > React-Native' 카테고리의 다른 글
[React Native] Collapsible Tab View 만들기 (10) | 2021.09.18 |
---|---|
[React Native] Animated Interpolate extrapolate (0) | 2021.08.16 |
[React Native] Star Rating 만들기 (3) | 2021.07.17 |
[React Native] Scroll bar 가운데로 오는 이슈 (2) | 2021.07.13 |
[React Native] Collapsible View 만들기 (0) | 2021.07.11 |