iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0

在這個章節我們要製作一個簡易的菜單收藏功能。當用戶按下收藏後,可以在收藏頁看到他收藏了哪些菜,並能刪除不要的收藏。
https://ithelp.ithome.com.tw/upload/images/20230923/20129635rZlMRHblpn.png
https://ithelp.ithome.com.tw/upload/images/20230923/201296353xgU0iJmNM.png

先製作兩個頁面元件,分別呈現菜單和收藏的內容:

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>
            … 省略

https://ithelp.ithome.com.tw/upload/images/20230923/201296358rw04xcbsT.png

回到 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>

參考:


上一篇
Day 22. 從製作語言切換功能,認識 Redux
下一篇
Day 24. 認識 Notifee 與基本功能
系列文
即使明天老闆突然叫你用 React Native 也可以跟他說好沒問題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言