iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0

現在的 hourlyWeather 資料只有顯示 4 筆,感覺可以顯示更多,所以我們來使用 FlatList 來顯示更多資料。

FlatList

FlatList 是 React Native 中用來顯示列表的元件,它的使用方式跟 React 中的 map 類似,都是將資料轉換成元件,然後顯示在畫面上。

使用 FlatList 的好處是,它可以只顯示畫面上可見的元件,而不是將所有元件都渲染出來,這樣可以節省記憶體,並且提升效能。

使用 FlatList 顯示資料

App.tsx 中,使用 FlatList 來顯示 hourlyWeather

import { View, SafeAreaView, ScrollView, FlatList } from 'react-native'

// 省略其他程式碼

export default function App() {
  // 省略其他程式碼

  return (
    <SafeAreaView className='flex-1 bg-blue-500'>
      <StatusBar style='auto' />
      <HeaderInfo />
      <CurrentWeather />
      <View className='flex-row py-6'>
        <FlatList
          data={hourlyWeather}
          keyExtractor={({ dt }) => dt.toString()}
          renderItem={({ item }) => {
            const { temp, readableTime } = item
            return (
              <HourlyForecast time={readableTime} icon='cloudy' temp={temp} />
            )
          }}
          horizontal
        />
      </View>
      // 省略其他程式碼
    </SafeAreaView>
  )
}

記得加上 horizontal,這樣 FlatList 才會水平顯示。

不過看一下畫面,會發現裡面的元件並沒有按照螢幕的寬度平均分配。

使用 useWindowDimensions

useWindowDimensions 是 React Native 提供的 Hook,可以取得螢幕的寬度與高度,我們可以使用它來設定 FlatListwidth。這在 React Native 中是很常見的做法,因為螢幕的寬度不一定是固定的,所以我們需要使用 useWindowDimensions 來取得螢幕的寬度,然後再設定給 FlatList

import {
  View,
  SafeAreaView,
  ScrollView,
  FlatList,
  useWindowDimensions
} from 'react-native'

// 省略其他程式碼

export default function App() {
  // 省略其他程式碼

  const windowWidth = useWindowDimensions().width
  const itemWidth = windowWidth / 4

  return (
    <SafeAreaView className='flex-1 bg-blue-500'>
      <StatusBar style='auto' />
      <HeaderInfo />
      <CurrentWeather />
      <View className='flex-row py-6'>
        <FlatList
          data={hourlyWeather}
          keyExtractor={({ dt }) => dt.toString()}
          renderItem={({ item }) => {
            const { temp, readableTime } = item
            return (
              <View style={{ width: itemWidth }}>
                <HourlyForecast time={readableTime} icon='cloudy' temp={temp} />
              </View>
            )
          }}
          horizontal
        />
      </View>
      // 省略其他程式碼
    </SafeAreaView>
  )
}

這裡我們使用 useWindowDimensions 取得螢幕的寬度,然後將寬度除以 4,這樣就可以得到每個元件的寬度,最後設定給 FlatList 中的元件。

這樣畫面就會顯示正常比例了。

接下來設定顯示筆數為 24 筆,這樣就可以顯示未來一天的天氣預報了。

// src/store/index.ts
const convertHourlyToReadableTime = (
  hourlyData: HourlyWeatherInfo[],
  sliceCount: number = 25
) => {
  return hourlyData.slice(1, sliceCount).map((hour: HourlyWeatherInfo) => {
    const readableTime = dayjs.unix(hour.dt).format('HH:mm')
    return { ...hour, readableTime }
  })
}

解決效能問題

雖然現在可以顯示未來一天的天氣預報,不過在終端機也出現一些警告訊息:

 LOG  VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. {"contentLength": 2358, "dt": 3517, "prevDt": 13971}

當看到這個訊息時,代表目前的效能不是很好,這通常出現在渲染大量元件時,所以我們需要優化一下。

App.tsx 中,我們可以將 FlatList 中的元件抽出來,然後使用 React.memo 來優化:

import { useEffect, memo } from 'react'

// 省略其他程式碼

export default function App() {
  // 省略其他程式碼

  const MemoizedHourlyForecast = memo(HourlyForecast)

  return (
    <SafeAreaView className='flex-1 bg-blue-500'>
      <StatusBar style='auto' />
      <HeaderInfo />
      <CurrentWeather />
      <View className='flex-row py-6'>
        <FlatList
          data={hourlyWeather}
          keyExtractor={({ dt }) => dt.toString()}
          renderItem={({ item }) => {
            const { temp, readableTime } = item
            return (
              <View style={{ width: itemWidth }}>
                <MemoizedHourlyForecast time={readableTime} icon='cloudy' temp={temp} />
              </View>
            )
          }}
          horizontal
        />
      </View>
      // 省略其他程式碼
    </SafeAreaView>
  )
}

這樣就不會出現警告訊息了,並且功能一切正常👍。


上一篇
Day 24 - 處理未來時間的氣象預報
下一篇
Day 26 - 處理未來一星期的天氣預報
系列文
掌握 React Native 與 Expo:30天雙打,新手也能精通跨平台開發!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言