上一篇文章建立完 Navigator,這篇文章會示範實作 Search 的部分,讓使用者搜尋完地區之後,直接顯示該地區的天氣資訊。
在開始做 Search
的畫面之前,先把該元件的狀態設定好。主要會有一個搜尋輸入欄位,所以需要一個輸入的狀態,這個狀態是一個字串。
interface Store {
// 省略其他狀態
searchCity: string
setSearchCity: (city: string) => void
}
const useStore = create<Store>((set) => ({
// 省略其他狀態
searchCity: '',
setSearchCity: (city) => set({ searchCity: city }),
)})
由於天氣資訊需要經緯度,所以這個搜尋輸入的字串,還需要轉換成經緯度,所以會建立一個方法來呼叫 Geocoding API
,並且把經緯度設定到 store
中。
interface Store {
// 省略其他狀態
searchCity: string
setSearchCity: (city: string) => void
fetchLatLngByCity: () => Promise<void>
}
const useStore = create<Store>((set) => ({
// 省略其他狀態
searchCity: '',
setSearchCity: (city) => set({ searchCity: city }),
fetchLatLngByCity: async () => {
const { searchCity } = useStore.getState()
if (searchCity) {
const apiUrl = process.env.EXPO_PUBLIC_GEOCODING_API_URL
const apiKey = process.env.EXPO_PUBLIC_GEOCODING_API_KEY
try {
const response = await fetch(
`${apiUrl}address=${encodeURIComponent(
searchCity
)}&key=${apiKey}&language=en`
)
const json = await response.json()
if (json?.results?.length > 0) {
const location = json.results[0]?.geometry?.location
useStore.getState().setLocation({
coords: {
latitude: location?.lat,
longitude: location?.lng,
altitude: null,
accuracy: null,
altitudeAccuracy: null,
heading: null,
speed: null
},
timestamp: Date.now()
})
} else {
console.error('Unable to fetch coordinates for the given city')
}
} catch (err) {
useStore
.getState()
.setErrorMsg('Failed to fetch coordinates for the given city')
}
} else {
console.warn('No search city provided')
}
)})
fetchLatLngByCity()
方法會先檢查 searchCity
是否有值,如果有值的話,就會呼叫 Geocoding API
,並且把經緯度設定到 store
的 location
。
接下來建立 Search.tsx
的畫面以及搜尋邏輯,會有一個輸入欄位和一個按鈕,按下按鈕之後會呼叫 fetchLatLngByCity
方法。
const Search = () => {
const { searchCity, setSearchCity, fetchLatLngByCity } = useStore()
const handleCityPress = () => {
fetchLatLngByCity()
}
const buttonText = searchCity ? 'Search' : 'Current Location'
return (
<SafeAreaView className='flex-1 bg-blue-500'>
<View className='items-center gap-y-4 mt-2'>
<TextInput
className='w-11/12 border border-gray-300 bg-white rounded-lg px-1 h-12'
onChangeText={setSearchCity}
value={searchCity}
placeholder='Search for a city...'
/>
<TouchableOpacity
className='w-2/3 bg-lime-500 rounded-full p-4'
onPress={handleCityPress}
>
<Text className='text-center'>{buttonText}</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
)
}
當 location
的值一改變,這時在 Home.tsx
的 useEffect
中就會觸發 fetchWeatherData
方法,並重新取得天氣資訊。
不過看完上面的 demo,可能會覺得搜尋完,還要自己點擊 Home 去看天氣資訊畫面,這樣的體驗不是很好,所以接下來會把搜尋完之後,直接導到 Home 畫面。
要實作導頁的功能,可以使用 React Navigation
的 Stack Navigator
,這個功能可以讓我們在不同的畫面之間切換,並且可以傳遞參數。
首先先安裝 Stack
的套件。
npm install @react-navigation/stack
在 Search.tsx
中,引入 StackNavigationProp
還有路徑的型別,最後修改 handleCityPress
方法,讓他可以導到 Home
畫面。
最後,因為還是要有一個顯示目前天氣的部分,這裡就設定為當搜尋欄位為空的時候,就會顯示目前位置的天氣資訊。
最後的成果如下: