iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 12
0
Modern Web

我不用Expo啦,React Native!系列 第 12

[Day12] 幫喜歡的項目按下愛心

今日關鍵字:redux


當初資料格式裡面有這兩個屬性

  isFavorite: boolean
  isReminding: boolean

對應著是否加入我的最愛及開啟提醒
今天先來使按鈕能正常發亮變暗吧

首先先幫細節頁面新增愛心跟鈴鐺兩個按鈕

...
        <TouchableOpacity activeOpacity={1}>
          <Ionicons
            style={{
              ...
              color: anime.isReminding ? 'tomato' : 'black'
            }}
            name={
              anime.isReminding ? iconType('heart') : iconType('heart-outline')
            }
            color="#000"
            size={24}
            onPress={() => {
            }}
          />
        </TouchableOpacity>
        <TouchableOpacity activeOpacity={1}>
          <Ionicons
            style={{
              ...
              color: anime.isFavorite ? 'tomato' : 'black'
            }}
            name={
              anime.isFavorite ? iconType('heart') : iconType('heart-outline')
            }
            color="#000"
            size={24}
            onPress={() => {
            }}
          />
        </TouchableOpacity>
...

依據isFavoriteisReminding的值圖標的樣式和顏色會有變化
https://ithelp.ithome.com.tw/upload/images/20200912/20121828I8zokUfpj7.png

根據day10的最後
點進細節頁面時

<AnimeDetail animeIndex={props.route.params!.anime} />

是把整個動畫物件傳了進來
那渲染細節頁面時要直接使用這個物件嗎?

No

由於現在的資料統一放在redux中
當我們等等寫一些action觸發狀態改變時
這樣寫無法即時接收狀態更新(也就是說點了愛心後圖標不會即時變色)


我承認我先這樣寫過了/images/emoticon/emoticon04.gif


接收更新後的狀態

因此這裡我決定只拿傳進來的動畫的id來用
先把所以動畫的資料以useSelector從store中獲得
再以id比對的方式尋找對應的資料

...
import { useSelector } from 'react-redux'
...
const AnimeDetail = ({ animeIndex }: AnimeDetailProps) => {
  ...
  const allAnime = useSelector((state: RootStateType) => state.allAnime)
  const anime = useMemo(() => {
    for (let i = 0; i < allAnime.length; i += 1) {
      if (allAnime[i].id === animeIndex.id) {
        return allAnime[i]
      }
    }
    return animeIndex
  }, allAnime)
  ...

為了減少運算,當store上的狀態未改變時不觸發重新尋找資料的動作


創造新Action

接收完狀態後,該來寫新的action來觸發狀態變化了

// action.ts
export const RENEW_DATA = 'RENEW_DATA'

export const renewData = (anime: Anime) => ({
  type: RENEW_DATA,
  payload: {
    anime
  }
})

由於以後還可能有其他更新資料
這裡將整個更改後的物件作為參數傳遞

reducer內則比對id
然後將資料更改對應的資料後回傳新陣列

// reducer.ts
  const allAnimeCopy = [...state]
  
  switch (action.type) {
    ...
    case animeActions.RENEW_DATA:
      for (let i = 0; i < state.length; i += 1) {
        if (action.payload!.anime!.id === allAnimeCopy[i].id) {
          allAnimeCopy[i] = action.payload!.anime!
        }
      }

      return allAnimeCopy

由於剛剛已經寫好接收狀態的動作
所以觸發action後細節頁面已經接收最新的狀態了

最後一步就是在細節頁面點擊按鈕時觸發action

// AnimeDetail.tsx
...
import { useDispatch, useSelector } from 'react-redux'
// import剛寫好的action
import { renewData } from '../../redux/action/animeAction'

const AnimeDetail = ({ animeIndex }: AnimeDetailProps) => {
  const dispatch = useDispatch()
  ...
        <TouchableOpacity activeOpacity={1}>
          <Ionicons
            ...
            onPress={() => {
              const animeCopy = { ...anime }
              animeCopy.isFavorite = !animeCopy.isFavorite
              dispatch(renewData(animeCopy))
            }}
          />
        </TouchableOpacity>
  ...

這樣就能將幫喜歡的動畫點愛心了
https://ithelp.ithome.com.tw/upload/images/20200912/20121828ZCK3YRmdyg.png


明天預計來寫在App內開啟網頁

然後今天就是延續day10,redux的基本操作,所以沒有參考啦/images/emoticon/emoticon31.gif


上一篇
[Day11] 圖片載入緩慢
下一篇
[Day13] 在App中瀏覽網頁
系列文
我不用Expo啦,React Native!33

尚未有邦友留言

立即登入留言