1. はじめに
デザイン事業部の長谷です。
今回は、「天気検索アプリの作成を通し、ReactやAPIへの理解を深めよう」の後編になります!
前編では、Viteを使用した開発環境の準備〜OpenWeatherMapのAPIキー取得、Reactの組み込みHooksを使用したデータ取得の実装について学びました。
先に前編をご覧いただいた方が、後編の内容がスムーズに入るかと思うので
まだご覧になられていない方は、前編もご覧下さい!
後編では、取得した天気データを表示するためのUIをMaterial-UI(MUI)とBulmaを使用し、簡単に作成していきます。
また今回アプリを作成しているのは、あくまでReactやAPIへの理解を深めるためになります。従って、「素早く・簡単」に作成するUIになりますので、ご容赦ください。
2. Material-UI(MUI)の導入
Material-UI、通称MUIは、Reactのためのデザインコンポーネントライブラリです。
複雑なUIを実現する場合には、難点が出てくるかもしれませんが、今回は「素早く・簡単」にが最重要ですので、その点MUIは優れていると思います。
以下が今回の手順になります。
1.MUIをインストールします。
(今回はMUIのアイコンのみ使用する為、この手順は飛ばしても、実装はできると思います)
npm install @mui/material @emotion/react @emotion/styled
2.MUIのアイコンをインストールします。
npm install @mui/icons-material
3.MUIのコンポーネントを使用し、天気アイコンを表示します。App.jsx
の中に以下のように、記述します。
import React, { useState, useEffect } from 'react'; // 以下文で使用するMUIのアイコンをインポート import { Search, WbSunny, WbCloudy, Opacity, ThunderstormTwoTone, GrainTwoTone } from '@mui/icons-material'; const WeatherApp = () => { // 状態管理用のuseState const [city, setCity] = useState(''); // 都市名が変更された際に天気データを取得するためのuseEffect useEffect(() => {}); // 天気状態に応じたアイコンを返す関数 const getWeatherIcon = (weatherCondition) => { const iconMap = { Clear: <WbSunny fontSize="large" />, Clouds: <WbCloudy fontSize="large" />, Rain: <Opacity fontSize="large" />, Drizzle: <GrainTwoTone fontSize="large" />, Thunderstorm: <ThunderstormTwoTone fontSize="large" />, }; return iconMap[weatherCondition] || null; }; return ( <div className="container"> {/* 検索バー */} <div className="is-flex is-justify-content-center is-align-items-center mb-2"> <Search fontSize="medium" /> <h1 className="title is-6 pr-2">地域検索</h1> </div> {/* 天気情報 */} {weatherData && ( <div className="box"> <div> {getWeatherIcon(weatherData.weather[0].main)} <p className="heading is-size-7">{weatherData.weather[0].description}</p> </div> </div> )} </div> ); }; export default WeatherApp;
上記のコードでは、weatherCondition
に応じて、適切な天気アイコンを表示しています。
3. Bulmaの導入
MUIのアイコンを無事使用できるようになったので、次はスタイルを当てていきます。
今回は「素早く・簡単」を求めるため、Bulmaを使用していきます。
ここで、Bulmaについて軽く説明します。
Bulmaとは、シンプルで使いやすいCSSフレームワークです。他の同種で例を挙げると、BootstrapやTailwind CSSがあります。これらのCSSフレームワークは基本的に、「読み込み・定義されているクラス名を付与」という一連の手段をとることで、クラス名に紐づいているスタイルを付与できます。
それぞれに特徴があり、Reactとの相性を考えると、実務で使用するのであれば、ReactとTailwind CSSの組み合わせがベストな気がします。しかし、今回は見栄えが悪くなりすぎない最低限のUIを実装できればいいので、手軽に利用できるBulmaを使用して、スタイルを付与していきます。
以下が具体的な手順です。
1.Bulmaをインストールします。
npm install bulma
2.Bulmaのスタイルシートをインポートします。App.jsx
の中に、以下の文を記述します。
import 'bulma/css/bulma.min.css';
3.Bulmaのクラスを使用し、天気情報を表示するコンポーネントを作成する。App.jsx
の中に、以下のように記述します。
import React from 'react'; import 'bulma/css/bulma.min.css'; const WeatherApp = () => { return ( <div className="container"> {/* 検索バー */} <div className="is-flex is-justify-content-center is-align-items-center mb-2"> <Search fontSize="medium" /> <h1 className="title is-6 pr-2">地域検索</h1> </div> <div className="field"> <div className="control"> <input className="input" type="text" placeholder="地域を入力" value={city} onChange={(e) => setCity(e.target.value)} /> </div> </div> {/* エラーメッセージ */} {error && <p className="help is-danger">{error}</p>} {/* 天気情報 */} {weatherData && ( <div className="box"> <h2 className="subtitle is-5 has-text-centered mb-2">{weatherData.name}</h2> <p className="is-size-6 has-text-centered mb-3">{formatDateTime(weatherData.forecast.list[0].dt_txt)}</p> <div className="level is-mobile mb-5"> <div className="level-item has-text-centered"> <div> {getWeatherIcon(weatherData.weather[0].main)} <p className="heading is-size-7">{weatherData.weather[0].description}</p> </div> </div> <div className="level-item has-text-centered"> <p className="title is-4">{weatherData.main.temp}<span className="is-size-6">°C</span></p> </div> </div> <div className="level is-mobile"> <div className="level-item has-text-centered"> <div> <p className="title is-6"> <span className="has-text-danger">{weatherData.forecast.list[0].main.temp_max}°C</span> / <span className="has-text-info">{weatherData.forecast.list[0].main.temp_min}°C</span> </p> </div> </div> <div className="level-item has-text-centered"> <div> <p className="title is-6 is-flex is-align-items-center"> <Opacity fontSize="small" /> {weatherData.forecast.list[0].pop * 100}% </p> </div> </div> </div> </div> )} </div> ); } export default WeatherInfo;
上記のコードでは、Bulmaのクラスを使用して、都市名、天気アイコン、気温、天気の説明を表示しています。
4. コンポーネントの実装
最後に、検索バーと天気情報を表示するコンポーネントを組み合わせて、アプリを完成させます。
以下が最終的なApp.jsx
のコードになります。
import React, { useState, useEffect } from 'react'; import axios from 'axios'; import { Search, WbSunny, WbCloudy, Opacity, ThunderstormTwoTone, GrainTwoTone } from '@mui/icons-material'; import 'bulma/css/bulma.min.css'; const API_KEY = 'MY_API_KEY'; const BASE_URL = 'https://api.openweathermap.org/data/2.5/weather'; const FORECAST_URL = 'https://api.openweathermap.org/data/2.5/forecast'; const WeatherApp = () => { // 状態管理用のuseState const [city, setCity] = useState(''); const [weatherData, setWeatherData] = useState(null); const [error, setError] = useState(null); // 都市名が変更された際に天気データを取得するためのuseEffect useEffect(() => { const fetchData = async () => { // 都市名が空の場合は処理を終了 if (!city) return; try { // 現在の天気データと予報データを並行して取得 const [weatherResponse, forecastResponse] = await Promise.all([ axios.get(BASE_URL, { params: { q: city, appid: API_KEY, units: 'metric', lang: 'ja' } }), axios.get(FORECAST_URL, { params: { q: city, appid: API_KEY, units: 'metric', lang: 'ja' } }), ]); // 取得したデータを組み合わせてweatherDataに設定 setWeatherData({ ...weatherResponse.data, forecast: forecastResponse.data }); setError(null); } catch (error) { // エラーが発生した場合はweatherDataをnullに設定し、エラーメッセージを表示 setWeatherData(null); setError('都市名を正しく入力して下さい。'); } }; fetchData(); }, [city]); // 天気状態に応じたアイコンを返す関数 const getWeatherIcon = (weatherCondition) => { const iconMap = { Clear: <WbSunny fontSize="large" />, Clouds: <WbCloudy fontSize="large" />, Rain: <Opacity fontSize="large" />, Drizzle: <GrainTwoTone fontSize="large" />, Thunderstorm: <ThunderstormTwoTone fontSize="large" />, }; return iconMap[weatherCondition] || null; }; // 日時データを日本語表記にフォーマットする関数 const formatDateTime = (dateTimeString) => { return new Date(dateTimeString).toLocaleString('ja-JP', { month: 'long', day: 'numeric' }); }; return ( <div className="container"> {/* 検索バー */} <div className="is-flex is-justify-content-center is-align-items-center mb-2"> <Search fontSize="medium" /> <h1 className="title is-6 pr-2">地域検索</h1> </div> <div className="field"> <div className="control"> <input className="input" type="text" placeholder="地域を入力" value={city} onChange={(e) => setCity(e.target.value)} /> </div> </div> {/* エラーメッセージ */} {error && <p className="help is-danger">{error}</p>} {/* 天気情報 */} {weatherData && ( <div className="box"> <h2 className="subtitle is-5 has-text-centered mb-2">{weatherData.name}</h2> <p className="is-size-6 has-text-centered mb-3">{formatDateTime(weatherData.forecast.list[0].dt_txt)}</p> <div className="level is-mobile mb-5"> <div className="level-item has-text-centered"> <div> {getWeatherIcon(weatherData.weather[0].main)} <p className="heading is-size-7">{weatherData.weather[0].description}</p> </div> </div> <div className="level-item has-text-centered"> <p className="title is-4">{weatherData.main.temp}<span className="is-size-6">°C</span></p> </div> </div> <div className="level is-mobile"> <div className="level-item has-text-centered"> <div> <p className="title is-6"> <span className="has-text-danger">{weatherData.forecast.list[0].main.temp_max}°C</span> / <span className="has-text-info">{weatherData.forecast.list[0].main.temp_min}°C</span> </p> </div> </div> <div className="level-item has-text-centered"> <div> <p className="title is-6 is-flex is-align-items-center"> <Opacity fontSize="small" /> {weatherData.forecast.list[0].pop * 100}% </p> </div> </div> </div> </div> )} </div> ); }; export default WeatherApp;
上記のコードでは、検索バーと天気情報を表示するコンポーネントを組み合わせています。handleCityChange
関数で、入力された都市名をcity
の状態に設定し、useEffect
内でデータ取得を行っています。
また、App.jsx内に付与したBulmaのスタイルだけだと要素が左右中央に配置されないと思うので、既存のindex.css
ファイル内に申し訳程度で以下コードを書き足していただければ、完成です。index.css
の中にも、以下スタイルを追記します。
/* 〜既存のコード〜 */ #root { margin-inline: auto; /* 「margin: 0 auto」でもOK */ }
5. アプリの完成
これで、簡単な天気検索アプリが完成しました。アプリを起動すると、以下のような画面が表示されます。
実際の使用イメージは以下になります。
6. まとめ
前後編を含め、本記事ではReactとOpenWeatherMap APIを使用して、簡単な天気検索アプリを作成する方法を紹介しました。いかがでしたでしょうか?
個人的にViteを使用した開発環境の準備、APIキーの取得、データ取得の実装、MUIとBulmaを使用したUIデザインについて学ぶことができました。
今後も、React・APIを使用して、ウェブアプリケーション開発の理解を深めていこうと思います。
本記事が誰かの役に立てば幸いです。
読んでいただきありがとうございました!