React Custom Toast with Higher Order Component
Build custom Toast for your React and React Native apps
In this article, I will show you the use of a Higher Order Component to wrap your container components with a Custom Toast component. The code snippets I will use in this article are from React Native. But the core concept will be the same for your React application. The only difference is I will use a few basic React Native components.
Benefits of this HOC:
- Improve code quality
- Follow the Signal Responsibility Principle
- Less code in your container component
withToast HOC
Below is the higher-order component that we will be using in your container components.
import React from 'react';
import Toast from '../../components/Toast';
// Define a generic type for the incoming component's props
export type ToastProps = {
showToast: (message: string) => void;
};
// This HOC takes a component and returns a new component
const withToast = <P extends object>(WrappedComponent: React.ComponentType<P>) => {
// Return a new functional component
return function WrappedWithToast(props: P) {
// Prepare some extra information
const [message, setMessage] = React.useState<string | undefined>(undefined);
function handleClear() {
setMessage(undefined);
}
function showToast(message: string) {
setMessage(message);
}
// Pass all incoming props through, and add new props
return (
<>
<WrappedComponent {...props as P} showToast={showToast} />
<Toast message={message} onClear={handleClear} />
</>
);
};
};
export default withToast;
- Here I have appended the parent component (WrappedComponent) with the Toast component.
- For the parent component, first I’m passing all the existing props and then adding new props that will give the parent component the ability to access the Toast component. In this case, I’m passing the showToast function as props.
Toast Component
Then below is our Toast component.
import React, { useEffect, useRef } from "react";
import { View, Text, StyleSheet, Animated } from "react-native";
interface Props {
message: string;
onClear: () => void;
}
export default function Toast({ message, onClear }: Props) {
const opacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
if (!message) return;
startAnimation();
const timer = setTimeout(() => {
stopAnimation();
}, 3000);
return () => {
clearTimeout(timer);
};
}, [message]);
const startAnimation = () => {
if (!message) return;
Animated.timing(opacity, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
};
const stopAnimation = () => {
Animated.timing(opacity, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start(() => {
onClear();
});
};
const scale = opacity.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: "clamp",
});
if (!message) return null;
return (
<Animated.View
style={[
styles.container,
{ transform: [{ scale }], opacity },
]}
>
<View style={styles.innerContainer}>
<Text style={styles.text}>{message}</Text>
</View>
</Animated.View>
);
}
const styles = StyleSheet.create({
container: {
position: "absolute",
top: 20,
width: "100%",
alignItems: "center",
backgroundColor: "transparent",
},
innerContainer: {
paddingHorizontal: 20,
paddingVertical: 12,
backgroundColor: "lightgrey",
borderRadius: 10,
shadowColor: "black",
shadowOffset: { width: 2, height: 4 },
shadowOpacity: 0.3,
},
text: {
color: "black",
},
});
- I have added some animations using AnimatedAPI to make the component more fancy.
- The important part is that I have passed the Toast message as a prop to this component.
- I won’t go into details about this component as it is a simple React component with some props and functions.
Now we are ready to use this Higher Order Component “withToast”.
Home Screen: Let’s use the withToast HOC
Below is the home screen where you want to show a toast as a requirement.
import React from "react";
import { View, StyleSheet, Button } from "react-native";
import withToast, { ToastProps } from "../hoc/withToast";
interface Props extends ToastProps {}
function HomeScreen(props: Props) {
return (
<View style={styles.container}>
<Button
title="Show Toast"
onPress={() => props.showToast("Toast from Home Screen")}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff",
},
});
export default withToast(HomeScreen);
- If you are using Typescript don’t forget to import ToastProps from the withToast component. That way you can get type support on what functions are available in the Toast component.
- Now you have to wrap your container with the withToast HOC. Then the withToast will do the rest for you.
Preview of the Toast component
There you go. We have successfully developed a HOC for Toast. Now you can reuse this HOC in all other container components where you need to show Toast messages.
Conclusion
In this article, I have shown you how we can use the Higher Oder Component concept in React to create our Custom Toast Component. We have followed the Single Responsibility Principle where the Container component does not have the responsibility anymore to render the Toast component.
I have developed this Toast component with minimum functionality. If you think this approach is suited for you, then you can add more features to it and come up with something amazing.
If you think there is a better way of doing this or if I have made a mistake do not hesitate to comment in this article. Hope you have learned something new. I will see you all in another article. Cheers!