export const API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";と設定する。(Secrets.js は、github には載せていないので各自作る必要がある)
#layoutContainer {
display: grid;
margin: 0;
min-height: 100vh;
grid-template-rows: 100px 1fr 100px;
grid-template-columns: 100px 1fr;
}
#layoutContainer .header {
grid-row: 1;
grid-column: 1 / span 2;
background: pink;
}
#layoutContainer .main {
grid-row: 2;
grid-column: 2;
background: lightgreen;
}
#layoutContainer .nav {
grid-row: 2;
grid-column: 1;
background: lightgrey;
}
#layoutContainer .footer {
grid-row: 3;
grid-column: 1 / span 2;
background: lightblue;
}
.weather {
width: 200px;
float: left;
padding-left: 20px;
}
button.link-style-btn{
cursor: pointer;
border: none;
background: none;
color: #0033cc;
}
button.link-style-btn:hover{
text-decoration: underline;
color: #002080;
}
import './App.css';
import { useState } from "react";
// 拡張子 js mjs jsx ts tsx は省略できる。将来 TypeScript 化しても import を変えなくていい
import Header from './Header'
import Menu from './Menu'
import Body from './Body'
import Footer from './Footer'
function App() {
const [CITIES, updateCities] = useState([]);
const callbackFromMenu = (cities) => {
updateCities(cities);
};
return (
<div id="layoutContainer">
{/* 子コンポーネントに、JSXを渡せる。子コンポーネント側からは、引数 children で取得できる */}
<div className="header">
<Header>
<h1>Weather Information</h1>
</Header>
</div>
<div className="nav"><Menu callback={callbackFromMenu}/></div>
<div className="body"><Body cities={CITIES}/></div>
{/* 子コンポーネントに、タグ属性でパラメータを渡せる(props)。 props には Javascript (配列や関数) も渡せる */}
<div className="footer"><Footer name="MYCompay" year="2024"/></div>
</div>
);
}
export default App;
<div id="layoutContainer">
<div className="header"> ヘッダー </div>
<div className="nav"> メニュー </div>
<div className="body"> ボディー </div>
<div className="footer"> フッター </div>
</div>
#layoutContainer {
display: grid;
margin: 0;
min-height: 100vh;
grid-template-rows: 100px 1fr 100px;
grid-template-columns: 100px 1fr;
}
#layoutContainer #header {
grid-row: 1;
grid-column: 1 / span 2;
background: pink;
}
...
vw | viewport width |
vh | viewport height |
vmax | max(vw, vh) |
vmin | min(vw, vh) |
function App() {
...
return (
<div id="layoutContainer">
{/* 子コンポーネントに、JSXを渡せる。子コンポーネント側からは、引数 children で取得できる */}
<div className="header">
<Header>
<h1>Weather Information</h1>
</Header>
</div>
<div className="nav"><Menu callback={callbackFromMenu}/></div>
<div className="body"><Body cities={CITIES}/></div>
{/* 子コンポーネントに、タグ属性でパラメータを渡せる(props)。 props には Javascript (配列や関数) も渡せる */}
<div className="footer"><Footer name="MYCompay" year="2024"/></div>
</div>
);
}
export default function Header({children}) {
return (
<>
{children}
</>
);
}
function App() {
...
return (
<div id="layoutContainer">
{/* 子コンポーネントに、JSXを渡せる。子コンポーネント側からは、引数 children で取得できる */}
<div className="header">
<Header>
<h1>Weather Information</h1>
</Header>
</div>
<div className="nav"><Menu callback={callbackFromMenu}/></div>
<div className="body"><Body cities={CITIES}/></div>
{/* 子コンポーネントに、タグ属性でパラメータを渡せる(props)。 props には Javascript (配列や関数) も渡せる */}
<div className="footer"><Footer name="MYCompay" year="2024"/></div>
</div>
);
}
import { useState } from 'react';
// function Footer (props) {...} と記述することもできる
// その場合は {props.year} {props.name} で参照する
export default function Footer({name, year}) {
// useState() の返り値は、配列 [値, 値を設定する関数]
const [yy, setYY] = useState( Number(year) );
const handleClick = () => {
setYY(yy+1);
};
return (
<>
<p align="center" onClick={handleClick}>
Copyright © {yy} {name} All Rights Reserved.
</p>
</>
);
}
const [yy, setYY] = useState( Number(year) ); const handleClick = () => { setYY(yy+1); };
Copyright © {yy} {name} All Rights Reserved.が新たな yy を使って再描画されることになる
function App() {
const [CITIES, updateCities] = useState([]);
const callbackFromMenu = (cities) => {
updateCities(cities);
};
return (
<div id="layoutContainer">
{/* 子コンポーネントに、JSXを渡せる。子コンポーネント側からは、引数 children で取得できる */}
<div className="header">
<Header>
<h1>Weather Information</h1>
</Header>
</div>
<div className="nav"><Menu callback={callbackFromMenu}/></div>
<div className="body"><Body cities={CITIES}/></div>
{/* 子コンポーネントに、タグ属性でパラメータを渡せる(props)。 props には Javascript (配列や関数) も渡せる */}
<div className="footer"><Footer name="MYCompay" year="2024"/></div>
</div>
);
}
import './App.css';
const JP_CITIES = ["Sapporo","Sendai","Tokyo","Nagoya","Kyoto","Osaka","Kobe","Hiroshima","Fukuoka","Okinawa"];
const CN_CITIES = ["Beijing","Chengdu","Chongqing","Dongguan","Guangzhou","Nanchong","Nanjing","Shanghai"
,"Shenzhen","Tianjin","Wuhan","Xi'an"];
const US_CITIES = ["Anchorage","Chicago","Dallas","Denver","Honolulu","Houston","Los Angeles","Miami","New York"
,"Palo Alto","Philadelphia","Phoenix","San Francisco","Seattle","Washington"
];
const EU_CITIES = ["Vienna","Brussels","Sofia","Copenhagen","Helsinki","Paris","Berlin","Athens","Nuuk","Reykjavik"
,"Dublin","Rome","Oslo","London"];
export default function Menu({callback}) {
const handleMenuClick = (area) => {
switch(area) {
case 'ja':
callback(JP_CITIES);
break;
case 'cn':
callback(CN_CITIES);
break;
case 'us':
callback(US_CITIES);
break;
case 'eu':
callback(EU_CITIES);
break;
default:
callback([]);
break;
}
};
return (
<ul>
{/* onClick に設定できるのは関数オブジェクトなので、引数渡したいときには無名関数を設定して、そこから引数付きでハンドラを呼び出す */}
<li><button className="link-style-btn" onClick={() => handleMenuClick('ja')}>Japan</button></li>
<li><button className="link-style-btn" onClick={() => handleMenuClick('cn')}>China</button></li>
<li><button className="link-style-btn" onClick={() => handleMenuClick('us')}>US</button></li>
<li><button className="link-style-btn" onClick={() => handleMenuClick('eu')}>Europe</button></li>
</ul>
);
}
function App() {
const [CITIES, updateCities] = useState([]);
const callbackFromMenu = (cities) => {
updateCities(cities);
};
return (
<div id="layoutContainer">
{/* 子コンポーネントに、JSXを渡せる。子コンポーネント側からは、引数 children で取得できる */}
<div className="header">
<Header>
<h1>Weather Information</h1>
</Header>
</div>
<div className="nav"><Menu callback={callbackFromMenu}/></div>
<div className="body"><Body cities={CITIES}/></div>
{/* 子コンポーネントに、タグ属性でパラメータを渡せる(props)。 props には Javascript (配列や関数) も渡せる */}
<div className="footer"><Footer name="MYCompay" year="2024"/></div>
</div>
);
}
import './App.css';
import React, { useState, useEffect } from "react";
import { API_KEY } from './Secrets';
import loading from './loading.gif';
// OpenWeatherMap API のキーは、Secrets.js に次のように定義する
// export const API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// Secrets.js は .gitignore にしています
// API Key は各自 https://openweathermap.org/api の Current Weather Data (無料) を取得してください
// 天気予報を表示するコンポーネント
function Weather({city, idx}) {
// 天気データを保持するステート
const [weather, setWeather] = useState(null);
// API から天気データを取得する関数
const fetchWeather = async () => {
try {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${API_KEY}`
);
const data = await response.json();
if (data.weather) {
setWeather(data);
} else {
console.error("Can't fetch weather:");
console.error(data);
}
} catch (error) {
console.error(error);
}
};
// コンポーネントがマウントされたときに API から天気データを取得する
useEffect(() => {
fetchWeather();
});
// 天気データがない場合はローディングを表示する
if (!weather) {
return (
<div className="weather">
<h2>{idx}. {city}</h2>
<p>Loading...</p>
<img src={loading} alt="loading"/>
</div>
);
}
// 天気データがある場合は、都市名、気温、天気アイコン、天気説明を表示する
return (
<div className="weather">
<h2>{idx}. {city}</h2>
<p>{weather.main.temp} ℃</p>
<img
src={`https://openweathermap.org/img/wn/${weather.weather[0].icon}.png`}
alt={weather.weather[0].description}
/>
<p>{weather.weather[0].description}</p>
</div>
);
};
export default function Body({cities}) {
return (
<>
{cities.map((elem,idx) => (
// List を展開するときには key (一意な値) を設定して、React.js に再レタリングが必要かのヒントを与える
<Weather key={elem} city={elem} idx={idx}/>
))}
</>
);
}