React components
Now we'd like to display multiple of these shopping list items without having to copy paste the code.
For this, we should pull the ShoppingListItem
into a separate component.
Tip
Add the no unused styles eslint plugin to easily detect when styles defined in a StyleSheet are not used in the file. It makes refactorings such as this much easier.
Instructions for configuring react-native/no-unused-styles
# if using yarn
yarn add -D eslint-plugin-react-native
# if using other package managers
npx expo install -- --save-dev eslint-plugin-react-native
and add the no unused styles rule to your .eslintrc.js
module.exports = {
extends: ["expo", "prettier"],
- plugins: ["prettier"],
+ plugins: ["prettier", "react-native"],
rules: {
"prettier/prettier": "error",
+ "react-native/no-unused-styles": "error",
},
};
Create a new component for components/ShoppingListItem.tsx and use it in App.tsx. Pass in the item name
as a prop.
As part of the refactor, use template strings to incorporate the item name
in the deletion confirmation message.
New file: components/ShoppingListItem.tsx
import { TouchableOpacity, View, Alert, StyleSheet, Text } from "react-native";
import { theme } from "../theme";
type Props = {
name: string;
};
export function ShoppingListItem({ name }: Props) {
const handleDelete = () => {
Alert.alert(
`Are you sure you want to delete ${name}?`,
"It will be gone for good",
[
{
text: "Yes",
onPress: () => console.log("Ok, deleting."),
style: "destructive",
},
{ text: "Cancel", style: "cancel" },
],
);
};
return (
<View style={styles.itemContainer}>
<Text style={styles.itemText}>{name}</Text>
<TouchableOpacity
onPress={handleDelete}
style={styles.button}
activeOpacity={0.8}
>
<Text style={styles.buttonText}>Delete</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
itemContainer: {
paddingVertical: 16,
paddingHorizontal: 8,
borderBottomColor: theme.colorCerulean,
borderBottomWidth: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
itemText: {
fontSize: 18,
fontWeight: "200",
},
button: {
backgroundColor: theme.colorBlack,
padding: 8,
borderRadius: 6,
},
buttonText: {
color: "#fff",
fontWeight: "bold",
textTransform: "uppercase",
letterSpacing: 1,
},
});
Update: App.tsx
@@ -1,33 +1,13 @@
-import { StyleSheet, Text, TouchableOpacity, View, Alert } from "react-native";
+import { StyleSheet, View } from "react-native";
import { theme } from "./theme";
+import { ShoppingListItem } from "./components/ShoppingListItem";
export default function App() {
- const handleDelete = () => {
- Alert.alert(
- "Are you sure you want to delete this?",
- "It will be gone for good",
- [
- {
- text: "Yes",
- onPress: () => console.log("Ok, deleting."),
- style: "destructive",
- },
- { text: "Cancel", style: "cancel" },
- ],
- );
- };
return (
<View style={styles.container}>
- <View style={styles.itemContainer}>
- <Text style={styles.itemText}>Coffee</Text>
- <TouchableOpacity
- onPress={handleDelete}
- style={styles.button}
- activeOpacity={0.8}
- >
- <Text style={styles.buttonText}>Delete</Text>
- </TouchableOpacity>
- </View>
+ <ShoppingListItem name="Coffee" />
+ <ShoppingListItem name="Tea" />
+ <ShoppingListItem name="Milk" />
</View>
);
}
@@ -38,28 +18,4 @@ const styles = StyleSheet.create({
backgroundColor: theme.colorWhite,
justifyContent: "center",
},
- itemContainer: {
- paddingVertical: 16,
- paddingHorizontal: 8,
- borderBottomColor: theme.colorCerulean,
- borderBottomWidth: 1,
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "space-between",
- },
- itemText: {
- fontSize: 18,
- fontWeight: "200",
- },
- button: {
- backgroundColor: theme.colorBlack,
- padding: 8,
- borderRadius: 6,
- },
- buttonText: {
- color: "#fff",
- fontWeight: "bold",
- textTransform: "uppercase",
- letterSpacing: 1,
- },
});
Checkpoint
Android | iOS |
---|---|
Checkpoint