iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0

上一篇文章建立完 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,並且把經緯度設定到 storelocation

建立畫面與搜尋邏輯

接下來建立 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.tsxuseEffect 中就會觸發 fetchWeatherData 方法,並重新取得天氣資訊。

不過看完上面的 demo,可能會覺得搜尋完,還要自己點擊 Home 去看天氣資訊畫面,這樣的體驗不是很好,所以接下來會把搜尋完之後,直接導到 Home 畫面。

使用 React Navigation 的 Stack Navigator

要實作導頁的功能,可以使用 React NavigationStack Navigator,這個功能可以讓我們在不同的畫面之間切換,並且可以傳遞參數。

首先先安裝 Stack 的套件。

npm install @react-navigation/stack

設定 Stack Navigator

Search.tsx 中,引入 StackNavigationProp 還有路徑的型別,最後修改 handleCityPress 方法,讓他可以導到 Home 畫面。

顯示目前位置的天氣資訊

最後,因為還是要有一個顯示目前天氣的部分,這裡就設定為當搜尋欄位為空的時候,就會顯示目前位置的天氣資訊。

最後的成果如下:


上一篇
Day 28 - 使用 React Navigation 切換頁面
下一篇
Day 30 - 使用 Lottie 動畫增加 App 視覺效果
系列文
掌握 React Native 與 Expo:30天雙打,新手也能精通跨平台開發!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言