在這個章節我們要製作一個簡易的菜單收藏功能。當用戶按下收藏後,可以在收藏頁看到他收藏了哪些菜,並能刪除不要的收藏。
先製作兩個頁面元件,分別呈現菜單和收藏的內容:
import React from 'react';
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
function MenuScreen({navigation}) {
const dishes = ['薯條', '漢堡', '玉米湯', '甜筒'];
return (
<View style={styles.container}>
<View style={styles.listContainer}>
{dishes &&
dishes.map(dish => (
<View style={styles.listItem} key={dish}>
<Text>{dish}</Text>
<TouchableOpacity>
<Text>收藏</Text>
</TouchableOpacity>
</View>
))}
</View>
<TouchableOpacity style={styles.btn}>
<Text
style={styles.btnTxt}
onPress={() => navigation.navigate('Favorite')}>
前往收藏頁
</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 10,
},
listItem: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingTop: 25,
paddingBottom: 5,
borderBottomWidth: 1,
borderBottomColor: '#999',
},
listContainer: {
marginBottom: 25,
},
btn: {
backgroundColor: '#888',
padding: 5,
},
btnTxt: {
textAlign: 'center',
color: '#fff',
},
});
export default MenuScreen;
import React from 'react';
import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
function FavoriteScreen() {
return (
<View style={styles.container}>
<View style={styles.listItem}>
<Text>收藏項目標題</Text>
<TouchableOpacity>
<Text>刪除</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 10,
},
listItem: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingTop: 25,
paddingBottom: 5,
borderBottomWidth: 1,
borderBottomColor: '#999',
},
});
export default FavoriteScreen;
為使用 navigation ,在 App.js 做 Stack 設定與載入:
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import MenuScreen from './pages/MenuScreen';
import FavoriteScreen from './pages/FavoriteScreen';
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Menu" component={MenuScreen} />
<Stack.Screen name="Favorite" component={FavoriteScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
前置作業完成後就能進入本節重點了。 react-native-async-storage 是一個能讓開發者在 React Native 環境中,將資料儲存在用戶本地端的套件。他會以非同步、未加密、持久的方式來儲存資料,支援 Android 、 iOS 、網站、 MacOS 和 Windows 多系統。開發者只要透過簡單 API ,即能輕鬆儲存、讀取、修改與刪除資料。
使用指令下載:
npm install @react-native-async-storage/async-storage
移動到 iOS 資料夾,安裝 Pod :
cd ios
pod install
接著引入 AsyncStorage 即可使用。首先來完成儲存功能,開啟 MenuScreen.js 。 AsyncStorage 和 LocalStorage 使用的方式很類似,透過 setItem 設定 key 和 value 。 key 是之後要取用資料時取用的名稱, value 則是要存進去的值。
import AsyncStorage from '@react-native-async-storage/async-storage';
function MenuScreen({navigation}) {
const storeData = async value => {
await AsyncStorage.setItem('menu', value);
};
return (
…省略
<TouchableOpacity onPress={() => storeData(dish)}>
<Text>收藏</Text>
</TouchableOpacity>
不過,如果我們要儲存一個陣列或物件的值到 key 中,則必須先轉為 JSON 字串才行。重新改寫上面的程式碼:
function MenuScreen({navigation}) {
const [favoriteList, setFavoriteList] = useState([]);
useEffect(() => {
const storeData = async () => {
await AsyncStorage.setItem('menu', JSON.stringify(favoriteList));
};
storeData();
}, [favoriteList]);
const handlePress = async value => {
setFavoriteList([...new Set([...favoriteList, value])]);
};
return (
… 省略
<TouchableOpacity onPress={() => handlePress(dish)}>
<Text>收藏</Text>
</TouchableOpacity>
要讀取目前存了哪些資料,則需要用 getItem 。記得如果剛剛有轉成 JSON 字串,要再轉回來:
function FavoriteScreen() {
const [favoriteList, setFavoriteList] = useState();
useEffect(() => {
const getData = async () => {
const value = await AsyncStorage.getItem('menu');
if (value) {
setFavoriteList(JSON.parse(value));
}
};
getData();
}, []);
return (
<View style={styles.container}>
{favoriteList &&
favoriteList.map(favoriteItem => (
<View style={styles.listItem} key={favoriteItem}>
<Text>{favoriteItem}</Text>
… 省略
回到 MenuScreen ,前面直接指定了 favoriteList 是空陣列,不過應該要取用目前儲存在本地端的資料才對。因此,把 getData 的 useEffect 也貼到 MenuScreen 裡:
function MenuScreen({navigation}) {
useEffect(() => {
const getData = async () => {
const value = await AsyncStorage.getItem('menu');
if (value) {
setFavoriteList(JSON.parse(value));
}
};
getData();
}, []);
來繼續製作刪除的功能。按下刪除時,設定新的 favoriteList ,再用 setItem 設定即可。
function FavoriteScreen() {
useEffect(() => {
if (favoriteList) {
const storeData = async () => {
await AsyncStorage.setItem('menu', JSON.stringify(favoriteList));
};
storeData();
}
}, [favoriteList]);
const handleDelete = index => {
const newList = favoriteList.filter((item, key) => key !== index);
setFavoriteList(newList);
};
return (
<View style={styles.container}>
{favoriteList &&
favoriteList.map((favoriteItem, index) => (
<View style={styles.listItem} key={favoriteItem}>
<Text>{favoriteItem}</Text>
<TouchableOpacity
onPress={() => handleDelete(index)}>
若要一次清除所有的 menu 裡的資料,則需透過 removeItem 這隻 API 。同時為了在清空後重新存取新的 AsyncStorage ,將 getData 從 useEffect 移出來外面呼叫。
function FavoriteScreen() {
useEffect(() => {
getData();
}, []);
const getData = async () => {
const value = await AsyncStorage.getItem('menu');
if (value) {
setFavoriteList(JSON.parse(value));
} else {
setFavoriteList([]);
}
};
const handleDeleteAll = async () => {
await AsyncStorage.removeItem('menu');
getData();
};
return (
… 省略
<TouchableOpacity style={styles.btn} onPress={handleDeleteAll}>
<Text style={styles.btnTxt}>清空全部</Text>