按照進度,這篇文章會示範怎麼從 OpenWeatherMap 取得未來數小時的天氣預報,並且讓資料展示在畫面上。
首先必須要先知道資料有哪些,以及怎麼取得。按照OpenWeatherMap 文件,可以看到有提供 hourly 的資料,並且是以每小時為單位,最多提供 48 小時的資料。
不過我們應該只需要取得“需要”的資料就好,而 OpenWeatherMap 提供了一個參數,讓我們可以去過濾不需要的資料,這個參數就是 exclude。
exclude 可以設定為以下幾種:
current:目前天氣minutely:每分鐘天氣hourly:每小時天氣daily:每日天氣alerts:天氣警報這裡我們暫時只需要 hourly 跟 current,所以 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 中,需要 dt、temp、weather、readableTime 這幾個資料,其中 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,這是因為第一筆資料是目前時間的天氣,所以我們不需要這筆資料。
接下來就可以從 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>
)
}
這樣就可以在畫面上看到未來數小時的天氣預報了:
