自分用のメモ
Contents
環境の準備
実行環境
- VS code:v1.62.3
- Node.js:v14.17.3
- React:v17.0.2
プロジェクトの作成(Typescriptで作成)
React Create Appで作成します。
天気情報の公開APIを取得する
React Create Appで作成されたソースの変更
fetchAPIの実装
- 24行目にAPIキーを設定します。
- 40行目で0.5秒待つようにして、Loadingアニメーションを見せるようにしています。
import { useState } from "react"
import './App.css'
import Title from './components/Title';
import Form from './components/Form';
import Results from './components/Results';
import Loading from "./components/Loading";
import { ResultsStateType } from "./components/ResultStateType";
const App = () => {
const [loading, setLoading] = useState<boolean>(false)
const [city, setCity] = useState<String>('')
const [results, setResults] = useState<ResultsStateType>({
country: '',
cityName: '',
temperature: '',
conditionText: '',
icon: ''
})
const getWeather = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault() //submitをキャンセル
setLoading(true)
// テンプレートリテラル「${}」を使う場合は、引用符をクォーテーションではなくバッククォートで囲む
const key = '自身で作成したAPIキー'
const url = `https://api.weatherapi.com/v1/current.json?key=${key}&q=${city}&aqi=no`
setTimeout(() => (
fetch(url)
.then(res => res.json())
.then(data => {
setResults({
country: data.location.country,
cityName: data.location.name,
temperature: data.current.temp_c,
conditionText: data.current.condition.text,
icon: data.current.condition.icon
})
setLoading(false)
})
.catch(err => alert('エラーが発生しました。'))
), 500)
}
return (
<div className="wrapper">
<div className="container">
<Title />
<Form setCity={setCity} getWeather={getWeather} />
{loading ? <Loading /> : <Results results={results} />}
</div>
</div>
)
}
export default App
CSSの実装(無くても動作します)
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Raleway&display=swap');
html {
font-size: 62.5%;
}
body {
font-family: 'Raleway','Noto Sans JP', sans-serif;
font-weight: 400;
font-size: 1.6rem;
font-display: swap;
color: #333;
margin: 0;
padding: 0;
}
/* App.js */
.wrapper {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
/* background: url("./background-image.jpg") center center no-repeat; */
background-size: cover;
}
.container {
width: 50vw;
background-color: aqua;
text-align: center;
padding: 40px 20px;
border-radius: 15px;
backdrop-filter: blur(20px);
box-shadow: 4px 4px 13px 5px rgba(0,0,0,0.25);
}
@media only screen and (max-width: 700px) {
.container{
width: 80vw;
}
}
/* Title.js */
h1 {
font-weight: 400;
margin: 0px 0 50px;
}
/* Form.js */
input[type="text"] {
background-color: transparent;
border: 0;
border-bottom: solid 1px #f15186;
width: 40%;
padding-bottom: 4px;
/* color: #fff !important; */
font-weight: lighter;
letter-spacing: 2px;
margin-bottom: 30px;
margin-right: 20px;
font-size: 20px;
}
button {
width: 40%;
border: 0;
padding: 8px 20px;
margin: 0 2px;
border-radius: 2px;
letter-spacing: 1px;
font-size: 1.5rem;
cursor: pointer;
background-color: #f15186;
color: #fff;
}
button:hover {
opacity: 0.9
}
*:focus {
outline: none;
}
/* Results.js */
.results-city {
font-size: 4rem;
}
.results-country {
font-size: 2rem;
}
.results-temp {
font-size: 6rem;
margin: 10px 0;
color: #f15186;
}
.results-temp > span {
font-size: 3rem;
color: #333;
}
.results-condition {
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
}
/* Loading.js */
.loading {
border: 4px solid #f15186;
border-top: 4px solid #ffffff;
border-radius: 50%;
width: 40px;
height: 40px;
margin: auto;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
components配下のソース
Titleの実装(HTMLのみ)
const Title = () => <h1>ワールドウェザー</h1>
export default Title
Formの実装(都市を入力するためのテキストボックスと実行ボタン)
- 7行目のpropsを分割代入しています。
import React from "react"
type FormPropsType = {
setCity: React.Dispatch<React.SetStateAction<String>>
getWeather: (e: React.FormEvent<HTMLFormElement>) => void
}
const Form = ({setCity, getWeather}: FormPropsType) => {
return (
<form onSubmit={getWeather}>
<input type='text' name='city' placeholder='都市名' onChange={e => setCity(e.target.value)} />
<button type='submit'>Get Weather</button>
</form>
)
}
export default Form
Resultsの実装(APIの取得結果を表示)
- 7行目のように、propsの数が多い場合は一旦変数化したほうが見やすい
import { ResultsStateType } from "./ResultStateType"
type ResultsPropsType = {
results: ResultsStateType
}
const Results = (props: ResultsPropsType) => {
const {country, cityName, temperature, conditionText, icon} = props.results
return (
<>
{cityName &&
<div>{cityName}</div>}
{country &&
<div>{country}</div>}
{temperature &&
<div>{temperature}<span>℃</span></div>}
{conditionText &&
<div className='result-condition'>
<img src={icon} alt='icon' />
<span>{conditionText}</span>
</div>}
</>
)
}
export default Results
Loadingの実装(実装自体はCSS)
const Loading = () => <div className='loading'></div>
export default Loading
Typeの実装(AppとResultsで共通化)
export type ResultsStateType = {
country: string
cityName: string
temperature: string
conditionText: string
icon: string
}
あとがき
特になし
コメントを残す