はじめに
今回はopenweatherのAPIを利用して天気情報を取得して
表示することを目標に作成していきました。
今回使用した技術
HTML
SASS
React
TypeScript
Redux ToolKit
初めてAPIを叩いてみた
先ほども述べたように今回はopenweatherのAPIを使用しました。
すでにopenweatherのAPIが用意されてたのであまり苦労せず、導入することができました!
しかし、地域名だけで取得するAPIだと「大阪」と検索しても大阪の天気を取得することができないので
あまり精度は高くないなと感じました。
fetchとaxiosの違いについて
APIを叩くにあたってサーバと通信する必要があります。
そこで必要になるのでfetchまたはaxiosです。
ここにつていは
①参考記事
②参考記事が分かりやく纏められてました!
大変だった点
今回のopenweatherのAPIは緯度経度から天気情報を取得するやり方を選択しました。
そのためには緯度経度を入力する必要がありました。
そこでgoogleMapのGeocodeを使用して緯度経度を取得したいと考えました。
googleMapの導入方法は簡単でgoogleMapの記事がとても完結に書かれててます!
苦労点①
緯度経度を取得する箇所で躓いたところがあります。
変更前のコードで実行すると
1回目「北海道」で検索。デフォルトで設定されてる東京の天気情報が取得できる。
2回目「大阪」で検索。北海道の天気情報が取得できる。
このように
①位置情報取得APIを実行する
②位置情報の取得完了する前に天気APIを実行している(更新前の位置情報を使っている)
③位置情報取得APIのレスポンスが返ってくる or 天気APIのレスポンスが帰ってくる
という順番で処理されてました。
理想は
①位置情報を取得するAPIを実行する
②位置情報が返ってくる(非同期処理)
③返ってきた位置情報を元に天気APIを実行する
に変更する必要がありました。
どのタイミングだったらAPIが返ってくるのかconsole.logを使いながらなんとか
位置情報を取得してから天気APIを実行することに成功しました。。。!!(半日近くかかりましたw)
結論,axiosする場所を変えました!
【変更前のコード】
//省略
const [city, setCity] = useState("");
const [latstate, setLatstate] = useState(35.6761919);
const [lngstate, setLngstate] = useState(139.7690174);
const weatherData = async (e:any) => {
dispatch(setLocationCity(city));
Geocode.setApiKey(APIKEY);
Geocode.fromAddress(city).then(
(response) => {
const searchPosition = {
lat: response.results[0].geometry,
lng: response.results[0].geometry
}
console.log(latstate);
console.log(lngstate);
},
(error) => {
alert("error");
}
);
e.preventDefault();
if (city === "") {
alert("値を追加してください");
} else {
await axios
.get(
`http://api.openweathermap.org/data/2.5/weather?lat=${latstate}&lon=${lngstate}&appid=${APIKEY_GEOCODE}`
)
.then((response) => {
console.log("status:", response.status); // 200
console.log("body:", response.data); // response body.
// catchでエラー時の挙動を定義する
})
.catch((err) => {
console.log("err:", err);
});
console.log(city);
setCity("");
}
};
【変更後コード】
const weatherData = async (e: any) => {
dispatch(setLocationCity(city));
Geocode.setApiKey(APIKEY);
Geocode.fromAddress(city).then(
async (response) => {
const { lat, lng } = response.results[0].geometry.location;
setLatstate(lat);
setLngstate(lng);
await axios
.get(
`http://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lng}&lang=ja&appid=${APIKEY_GEOCODE}`
)
.then((response) => {
const data: any = response.data;
setWeather(data);
console.log(weather);
console.log("status:", response.status);
})
.catch((err) => {
console.log("err:", err);
});
},
(error) => {
alert("error");
}
);
e.preventDefault();
if (city === "") {
alert("値を追加してください");
} else {
console.log(city);
setCity("");
}
};
苦労点②
グラフで1時間ごとの時間だけを取得したく、for文を使って取得したい時に無限ループに陥ってしまったことです。
陥った原因として、if文でデータが渡ってきた時だけ実行するコードを記述したところ無限ループが発生しました。
for文の中でuseStateを更新したり。。。←無茶苦茶ですね
そこでuseEffectを使用してデータが渡ってきた時のみコードを実行することを行いました。
useEffectの存在は知っていましたが、いまいち使いどころが分からず敬遠しがちでしたが、今回の経験でグッと理解することができました!
【無限ループコード】
const Chart = (props: any) => {
const [nowtimes, setNowTimes] = useState<number[]>([]);
const weatherData = props.data;
const honlyDatas = weatherData.hourly;
if (honlyDatas) {
let time = [];
for (let i = 0; i < 7; i++) {
time.push(new Date(honlyDatas[i].dt * 1000).getHours());
setNowTimes(time);
}
}
【修正コード】
useEffect(() => {
const weatherData = props.data;
const honlyDatas = weatherData.hourly;
console.log(honlyDatas);
if (honlyDatas) {
let timeList = [];
let temperature = [];
for (let i = 0; i < 7; i++) {
timeList.push(new Date(honlyDatas[i].dt * 1000).getHours());
}
setNowTimes(timeList);
for (let i = 0; i < 7; i++) {
temperature.push(Math.fround(honlyDatas[i].temp - 273.15));
}
setTempHourly(temperature);
}
}, [props.data]);
出てきた課題
①TypeScriptの型定義をany型に逃げてしまう。
→頭ではany型を使用しないと思っていてもオブジェクトなどの時はany型で指定してしまってた。
※要復習!!※
②自分でコードを見返した時に追えない時がある。
理由として
①変数に分かりやすがない
②このコードは何をしてるというコメントがない
上記2点が主な原因かなと感じました。
①に関しては誰が見ても容易に想像できる変数名をつける
②は、コードを読み直す時間がもったいないので、最初は時間がかかってもコメントを残して読み返す時間を短くする
次回から意識して取り組みたいと思います。