iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0
Mobile Development

從零開始學React Native系列 第 14

【從零開始學React Native】13. 創建Todo Tracker——存儲資料在本地

  • 分享至 

  • xImage
  •  

我們先安裝MMKV來管理我們的資料。react-native-mmkv,這裡安裝2.12.2版本。記得要重新build。

創建一個用來操作Task的service。

// src\services\task.service.tsx

import { MMKV } from 'react-native-mmkv';
import { Task } from '../types';

const storage = new MMKV();

const TASKS_KEY = 'tasks';

export const TaskStorage = {
  saveTasks: (tasks: Task[]) => {
    storage.set(TASKS_KEY, JSON.stringify(tasks));
  },

  getTasks: (): Task[] => {
    const tasksJson = storage.getString(TASKS_KEY);
    return tasksJson ? JSON.parse(tasksJson) : [];
  },

  addTask: (task: Task) => {
    const tasks = TaskStorage.getTasks();
    tasks.push(task);
    TaskStorage.saveTasks(tasks);
  },

  updateTask: (updatedTask: Task) => {
    const tasks = TaskStorage.getTasks();
    const index = tasks.findIndex(task => task.id === updatedTask.id);
    if (index !== -1) {
      tasks[index] = updatedTask;
      TaskStorage.saveTasks(tasks);
    }
  },

  deleteTask: (taskId: string) => {
    const tasks = TaskStorage.getTasks();
    const updatedTasks = tasks.filter(task => task.id !== taskId);
    TaskStorage.saveTasks(updatedTasks);
  },
};

並修改我們的atorm中的task:

import { atom } from 'jotai';
import { Task, User } from '../types';
import { TaskService } from '../services/task.service';

export const userAtom = atom<User | null>(null);
export const tasksAtom = atom<Task[]>(TaskService.getTasks());

接著,我們來一一更新 home.page.tsxadd-task.page.tsxtask-detail.page.tsx 這些頁面。

更新home page

// src\pages\home.page.tsx

// ...

type HomePageProps = {
  isDarkMode: boolean;
  onTaskSelect?: (task: Task) => void;
};

const HomePage: React.FC<HomePageProps> = ({ isDarkMode, onTaskSelect }) => {
  const [tasks, setTasks] = useAtom(tasksAtom);

  useEffect(() => {
    const loadedTasks = TaskService.getTasks();
    setTasks(loadedTasks);
  }, [setTasks]);

  const toggleTaskStatus = useCallback((id: string) => {
    const updatedTasks = tasks.map(task =>
      task.id === id ? { ...task, isDone: !task.isDone } : task
    );
    setTasks(updatedTasks);
    TaskService.saveTasks(updatedTasks);
  }, [tasks, setTasks]);

  const deleteTask = useCallback((id: string) => {
    const updatedTasks = tasks.filter(task => task.id !== id);
    setTasks(updatedTasks);
    TaskService.deleteTask(id);
  }, [tasks, setTasks]);

  const renderRightActions = useCallback((id: string) => {
    return (
      <TouchableOpacity
        style={styles.deleteButton}
        onPress={() => deleteTask(id)}
      >
        <Text style={styles.deleteButtonText}>Delete</Text>
      </TouchableOpacity>
    );
  }, [deleteTask]);

  // ...

更新add頁面中的handleSubmit

// src\pages\add-task.page.tsx

import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView, Alert } from 'react-native';
import { useSetAtom } from 'jotai';
import { tasksAtom } from '../stores/atoms';
import { Task, Tags } from '../types/index';
import DatePicker from 'react-native-date-picker';
import { TaskService } from '../services/task.service';

type AddTaskPageProps = {
  isDarkMode: boolean;
};

const AddTaskPage: React.FC<AddTaskPageProps> = ({ isDarkMode }) => {
  // ...

  const handleSubmit = () => {
    const newTask: Task = {
      id: Date.now().toString(),
      title,
      startDate: startDate ? startDate.toISOString().split('T')[0] : null,
      endDate: endDate ? endDate.toISOString().split('T')[0] : null,
      description,
      isDone: false,
      tags,
      subTasks: [],
    };

    const validationErrors = validateTask(newTask);

    if (validationErrors.length > 0) {
      Alert.alert('Validation Error', validationErrors.join('\n'));
      return;
    }

    TaskService.addTask(newTask);
    setTasks(prevTasks => [...prevTasks, newTask]);
    // Reset form fields
    setTitle('');
    setDescription('');
    setStartDate(null);
    setEndDate(null);
    setTags([]);
    Alert.alert('Success', 'Task added successfully');
  };

接著修改一下detail部分。

// src\pages\task-detail.page.tsx
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Switch, TextInput, ScrollView } from 'react-native';
import { Task } from '../types';
import { useAtom } from 'jotai';
import { tasksAtom } from '../stores/atoms';
import { TaskService } from '../services/task.service';

type TaskDetailsPageProps = {
  isDarkMode: boolean;
  task: Task;
  onBack: () => void;
};

const TaskDetailsPage: React.FC<TaskDetailsPageProps> = ({
  isDarkMode,
  task,
  onBack,
}) => {
  const [isEditing, setIsEditing] = useState(false);
  const [editedTask, setEditedTask] = useState(task);
  const [, setTasks] = useAtom(tasksAtom);

  const handleEdit = useCallback(() => {
    setIsEditing(true);
  }, []);

  const handleSave = useCallback(() => {
    TaskService.updateTask(editedTask);
    setTasks(prevTasks => prevTasks.map(t => t.id === editedTask.id ? editedTask : t));
    setIsEditing(false);
  }, [editedTask, setTasks]);

  const handleDelete = useCallback(() => {
    TaskService.deleteTask(task.id);
    setTasks(prevTasks => prevTasks.filter(t => t.id !== task.id));
    onBack();
  }, [task.id, setTasks, onBack]);

  const toggleCompletion = useCallback(() => {
    const updatedTask = { ...editedTask, isDone: !editedTask.isDone };
    setEditedTask(updatedTask);
    TaskService.updateTask(updatedTask);
    setTasks(prevTasks => prevTasks.map(t => t.id === updatedTask.id ? updatedTask : t));
  }, [editedTask, setTasks]);

  // ...

心得

目前我們已經將部分的Task資料存儲方法和操作改成使用了MMKV,明天會繼續把User部分也使用相關的本地存儲方法。


上一篇
【從零開始學React Native】12. 關於React Native的本地存儲
系列文
從零開始學React Native14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言