iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0

按照進度,這篇文章會示範怎麼從 OpenWeatherMap 取得未來數小時的天氣預報,並且讓資料展示在畫面上。

了解 API

首先必須要先知道資料有哪些,以及怎麼取得。按照OpenWeatherMap 文件,可以看到有提供 hourly 的資料,並且是以每小時為單位,最多提供 48 小時的資料。

不過我們應該只需要取得“需要”的資料就好,而 OpenWeatherMap 提供了一個參數,讓我們可以去過濾不需要的資料,這個參數就是 exclude

exclude 可以設定為以下幾種:

  • current:目前天氣
  • minutely:每分鐘天氣
  • hourly:每小時天氣
  • daily:每日天氣
  • alerts:天氣警報

這裡我們暫時只需要 hourlycurrent,所以 exclude 設定為 daily,minutely,alerts

設定狀態

了解完 API 之後,我們就可以開始設定狀態了。首先,我們需要一個狀態來存放未來數小時的天氣預報,這裡我們使用 hourlyWeather 來表示,並設定型別:

interface HourlyWeatherInfo {
  dt: number
  temp: number
  weather: WeatherDetail[]
  readableTime: string
}

interface Store {
  // 省略其他程式碼
  hourlyWeather: HourlyWeatherInfo[] | null
}

const useStore = create<Store>((set) => ({
  // 省略其他程式碼
  hourlyWeather: null,
  fetchWeatherData: async () => {
    const { location, errorMsg } = useStore.getState()
    if (location && location.coords) {
      const { latitude, longitude } = location.coords
      const apiUrl = process.env.EXPO_PUBLIC_WEATHER_API_URL
      const apiKey = process.env.EXPO_PUBLIC_WEATHER_API_KEY
      try {
        const response = await fetch(
          `${apiUrl}lat=${latitude}&lon=${longitude}&exclude=minutely,daily,alerts&units=metric&appid=${apiKey}`
        )
        const json = await response.json()
        set({
          currentWeather: json.current
        })
      } catch (err) {
        console.log(err)
      }
    }
  }
}))

看一下下面圖片需要展示什麼資料:

所以我們在 HourlyWeatherInfo 中,需要 dttempweatherreadableTime 這幾個資料,其中 dt 是時間戳記,temp 是溫度,weather 是天氣資料,readableTime 是可讀的時間。

轉換時間戳記

由於需要展示可讀的時間,所以我們需要將時間戳記轉換成可讀的時間,這裡我們使用上一篇文章介紹的 dayjs 來轉換時間戳記:

import dayjs from 'dayjs'

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

這裡我們使用 dayjs.unix 來轉換時間戳記,並且設定格式為 HH:mm。而且因為時間資料有 48 筆,我們不需要這麼多,所以使用 slice 來過濾掉不需要的資料,先取得 4 筆資料就好。

但是眼尖的你可能會發現,這裡的 slice 是從 1 開始,而不是 0,這是因為第一筆資料是目前時間的天氣,所以我們不需要這筆資料。

從 API 取得資料

接下來就可以從 fetchWeatherData() 中取得資料,並且轉換成可讀的時間:

const useStore = create<Store>((set) => ({
  // 省略其他程式碼
  fetchWeatherData: async () => {
    const { location, errorMsg } = useStore.getState()
    if (location && location.coords) {
      const { latitude, longitude } = location.coords
      const apiUrl = process.env.EXPO_PUBLIC_WEATHER_API_URL
      const apiKey = process.env.EXPO_PUBLIC_WEATHER_API_KEY
      try {
        const response = await fetch(
          `${apiUrl}lat=${latitude}&lon=${longitude}&exclude=minutely,daily&units=metric&appid=${apiKey}`
        )
        const json = await response.json()
        set({
          currentWeather: json.current,
          hourlyWeather: convertHourlyToReadableTime(json.hourly)
        })
      } catch (err) {
        console.log(err)
      }
    }
  },
}))

這裡我們使用 convertHourlyToReadableTime() 取得來自 hourly 的資料。

顯示資料

最後在 App.tsx 中,從 useStore 中取得 hourlyWeather,並且將資料傳遞給 HourlyWeather

export default function App() {
  const {
    location,
    errorMsg,
    setLocation,
    setErrorMsg,
    fetchWeatherData,
    fetchLocation,
    hourlyWeather
  } = useStore()

  // 省略其他程式碼

  return (
    <SafeAreaView className='flex-1 bg-blue-500'>
      <StatusBar style='auto' />
      <HeaderInfo />
      <CurrentWeather />
      <View className='flex-row justify-around p-6'>
        {hourlyWeather?.map((hourly) => {
          const { temp, readableTime, dt } = hourly
          return <HourlyForecast time={readableTime} icon="cloudy" temp={temp} key={dt} />
        })}
      </View>
      // 省略其他程式碼
    </SafeAreaView>
  )
}

最後記得將 HourlyWeather 的型別修正,並放入 props:

interface HourlyForecastProps {
  time: string
  icon: keyof typeof weatherIcons
  temp: number
}

const HourlyForecast: FC<HourlyForecastProps> = ({ time, icon, temp }) => {
  const IconComponent = weatherIcons[icon]?.component || AntDesign || Feather
  const iconName = weatherIcons[icon]?.name || 'question'
  const iconColor = weatherIcons[icon]?.color || '#ffffff'

  return (
    <View className='items-center space-y-2'>
      <Text className='text-sm font-medium text-white'>{time}</Text>
      <IconComponent name={iconName} size={30} color={iconColor} />
      <Text className='text-lg font-semibold text-white'>{temp}°</Text>
    </View>
  )
}

這樣就可以在畫面上看到未來數小時的天氣預報了:


上一篇
Day 23 - 顯示現在時間
下一篇
Day 25 - 使用 useWindowDimensions 讓元件自適應螢幕寬度
系列文
掌握 React Native 與 Expo:30天雙打,新手也能精通跨平台開發!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言