iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0

昨天介紹了如何使用 React Native 的 Modal 來實作一個確認刪除的提示 Modal,今天要做的則是如何使用 Modal 來編輯原本的待辦事項。

在 Context Store 加入編輯 Modal 顯示的狀態與其控制方法

為了支援待辦事項的編輯功能,首先需要在 context store 中加入一些新的狀態值以及對應的 action 處理方法。

首先,在 initialState 中加入 modalVisible 中的 editeditText 兩個新的狀態。其中,edit 用於控制編輯 Modal 的顯示狀態,而 editText 則用於儲存目前的編輯內容。

// src/store/index.js:
const initialState = {
  inputText: '',
  todos: [],
  modalVisible: {
    confirm: false,
    edit: false
  },
  chooseId: null,
  editText: ''
}

接著,我們在 reducer 中加入幾個新的 action 處理還有修正原本的:

  • SET_CHOOSE_ID: 設定當前選擇的待辦事項ID,並初始化 editText 為該待辦事項的內容。
  • EDIT_ITEM: 編輯指定ID的待辦事項內容。
  • SET_EDIT_TEXT: 設定目前的編輯內容。

這三個 action handlers 的實現如下:

case 'SET_CHOOSE_ID':
  return {
    ...state,
    chooseId: action.payload,
    editText: state.todos.find((todo) => todo.id === action.payload).text
  }

case 'EDIT_ITEM':
  return {
    ...state,
    todos: state.todos.map((todo) =>
      todo.id === action.payload.id
        ? { ...todo, text: action.payload.text }
        : todo
    )
  }

case 'SET_EDIT_TEXT':
  return {
    ...state,
    editText: action.payload
  }

新增 EditModal 元件

有了上面的準備,接下來可以開始新增一個 EditModal 元件,用於顯示編輯界面。

這個元件主要包含以下部分:

  • 一個 TextInput 用於輸入和編輯待辦事項的內容。
  • 兩個按鈕,一個用於確認修改,一個用於取消修改。

元件的程式碼如下:

import { useContext } from 'react'
import {
  Modal,
  View,
  Text,
  TouchableOpacity,
  TextInput,
  StyleSheet
} from 'react-native'
import { Store } from '../store'

const EditModal = () => {
  const { state, dispatch } = useContext(Store)
  const { modalVisible, chooseId, editText } = state

  const closeModel = () => {
    dispatch({
      type: 'SET_MODAL_VISIBLE',
      payload: { name: 'edit', visible: false }
    })
  }

  const handleSetText = (text) => {
    dispatch({ type: 'SET_EDIT_TEXT', payload: text })
  }

  const handleConfirmEdit = () => {
    dispatch({ type: 'EDIT_ITEM', payload: { id: chooseId, text: editText } })
    closeModel()
  }

  return (
    <View style={styles.container}>
      <Modal
        animationType='fade'
        transparent={true}
        visible={modalVisible.edit}
        onRequestClose={closeModel}
      >
        <View style={styles.container}>
          <View style={styles.modalWrap}>
            <TextInput
              value={state.editText}
              onChangeText={handleSetText}
              autoCapitalize='none'
              placeholder='請輸入事項'
              style={styles.input}
            />
            <View style={styles.buttons}>
              <TouchableOpacity
                onPress={closeModel}
                style={[styles.button, { backgroundColor: '#f66162' }]}
              >
                <Text style={{ color: '#fff' }}>取消修改</Text>
              </TouchableOpacity>
              <TouchableOpacity
                onPress={handleConfirmEdit}
                style={[styles.button, { backgroundColor: '#a1f64c' }]}
              >
                <Text>確定修改</Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      </Modal>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'rgba(0,0,0,0.5)'
  },
  modalWrap: {
    paddingHorizontal: 15,
    paddingTop: 50,
    paddingBottom: 30,
    borderRadius: 10,
    backgroundColor: '#fff',
    width: '95%'
  },
  buttons: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center',
    marginTop: 40
  },
  button: {
    padding: 10,
    borderRadius: 5
  },
  input: {
    borderColor: '#bcb9b9',
    borderWidth: 1,
    borderRadius: 5,
    paddingHorizontal: 15,
    fontSize: 20,
    backgroundColor: '#fff',
    height: 50
  }
})

export default EditModal

最後,我們需要在 App.js 中導入並使用這個新增的 EditModal 元件。

import EditModal from './src/components/EditModal'

export default function App() {
  return (
    <StoreProvider>
      <RootSiblingParent>
        <SafeAreaView style={styles.container}>
          <Head />
          <List />
          <ConfirmModal />
          <EditModal />
          <StatusBar style='auto' />
        </SafeAreaView>
      </RootSiblingParent>
    </StoreProvider>
  )
}

在 List 控制 EditModal

編輯功能的觸發點在於待辦事項列表,所以我們需要在 List 元件中加入相應的控制邏輯。

首先,我們定義一個 handleEditText 的函式,它接受一個待辦事項 ID 作為參數。當使用者想要編輯一個待辦事項時,我們首先使用 SET_CHOOSE_ID 來設定當前選擇的待辦事項ID,然後打開 EditModal

const handleEditText = (id) => {
    dispatch({ type: 'SET_CHOOSE_ID', payload: id })
    dispatch({ type: 'SET_MODAL_VISIBLE', payload: { name: 'edit', visible: true } })
  }

並在 <Item /> 新增一個 onEditText props 並將 handleEditText 帶入。

 const renderItem = ({ item }) => (
    <Item
      item={item}
      onToggleItem={() => handleToggleItem(item.id)}
      onConfirmDelete={() => handleConfirmDelete(item.id)}
      onEditText={() => handleEditText(item.id)}
    />
  )

此外,我們還需要在每個待辦事項的呈現中,綁定這個 handleEditText 到對應的操作上,這裡我們設定在點擊文字的行為上。

const Item = ({ item, onToggleItem, onConfirmDelete, onEditText }) => (
  <View style={styles.item}>
    <View style={styles.checkboxAndText}>
      <Checkbox
        value={item.checked}
        onValueChange={onToggleItem}
        color={item.checked && '#1dee98'}
      />
      <Text onPress={onEditText} style={[styles.itemText, item.checked && styles.finishItemText]}>
        {item.text}
      </Text>
    </View>
    <TouchableOpacity onPress={onConfirmDelete}>
      <MaterialCommunityIcons name='delete-circle' size={24} color='#ED6070' />
    </TouchableOpacity>
  </View>
)

編輯 Demo

總結

今天我們新增了一個 Modal,並且用它來編輯待辦事項,其實跟昨天的刪除提示 Modal 蠻像的,只是編輯所需要掌握的狀態控制更多了一點。

下一篇文章,將會示範如何將待辦事項保存到本地儲存,即使 App 被關閉或者重啟後,待辦事項的內容仍然可以被保留。


上一篇
Day 12 - 使用 Modal 提示使用者是否刪除
下一篇
Day 14 - 儲存待辦事項到本地儲存
系列文
掌握 React Native 與 Expo:30天雙打,新手也能精通跨平台開發!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言