はじめに
【作成にあたり学習した事】
- アプリをブートストラップする create-react-app
- TypeScriptでReactを使用する
- Reactフック
- 非同期通信を開き天気API
- 基本的なスタイリング
プログラムを書くポイント
- 小さく作って小さく動かす。
- 最初にタスクを明確にして、作成順を決める。
天気アプリのタスクの明確化と作成順
場所を検索して現在の天気と24時間の天気予報を表示できるシンプルな天気アプリを作成。
プロジェクト用の新しいフォルダーを作成する
npx create-react-app <プロジェクト用の新しいフォルダー名> --template typescript
cd <プロジェクト用の新しいフォルダー>
これにより、プロジェクトフォルダーが作成され、npm install(またはyarnインストールされていることが検出された場合に)実行される。
ファイル削除と書き換え
1.App.css及び内容の削除index.css
2.logo.svgからのインポートを削除しますApp.tsx
3.削除App.test.tsxしてlogo.svg
4.アプリ関数App.tsx(コンポーネント)の定義を次のように書き換える。
function App() {
return (
<div>
<h1>Weather App</h1>
</div>
);
}
場所の表を表示する
① HTMLの場合と同じ方法でビューを構築
仕様:ユーザーが場所を検索できる入力を追加し、その場所をテーブルに追加する。⇨ 場所は文字列になり、テーブル内のすべての場所のリストは文字列配列になる。
function App() {
return (
<div>
<h1>Weather App</h1>
<div>
<label>
Add Location <input type="text" value="Paris"/>
</label>
<button>Search</button>
</div>
<div>
<h2>Locations</h2>
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr><td>Belfast</td></tr>
<tr><td>New York</td></tr>
</tbody>
</table>
</div>
</div>
);
}
入力の内容を表すための状態を導入
『状態』とは、ビューに ※ バインドされているデータのこと。状態が変化した場合にのみ、Reactが再レンダリングしてビューを更新する。状態は、Reactアプリの信頼できる情報源。
※ バインド 【bind】 ⇨ 設定値や入出力要素、プログラム上の変数など、種類の異なる複数の要素を互いに関連付け、互いに機能を利用したり、データを送受信できるようにしたり、変更を自動的に反映させたりできるようにする仕組みをこのように呼ぶことが多い。
useStateフックを使用して機能コンポーネントの状態を紹介。フックは関数の先頭に移動する必要があり、useStateフックは状態の現在の値を含むタプルと、1行で分解できる状態を更新する関数を返す。
import React, {useState} from 'react';
// ...
function App() {
const [locationSearch, setLocationSearch] = useState('Paris');
// ...
'Paris'は初期値であり、タイプが推測される。特定の状態を識別するための識別子や名前はない。呼び出しの一定の順序は、状態を識別する方法で、フックは関数の開始時に移動し、などの制御フロー内に配置することはできない。
次にlocationSearch、JSXに値を含める必要があり、以下を使用して、JSXに式を埋め込むことができる{}。
<label>
Add Location <input type="text" value={locationSearch}/>
</label>
値属性の割り当てでは引用符が使用されていないことに注意。Reactで式を使用して属性を割り当てるときは、常にattribute={expression}ではなくを使用するattribute="{expression}"。
最後に、コントロールが変更されるたびに関数onChangeを呼び出すハンドラー(キャメルケースに注意)を追加する必要があり、setLocationSearchする。
ここでも、{}矢印関数式を提供するために使用する。したがって、変更ハンドラーが起動すると、状態が設定され、それにより、再レンダリングがトリガーされる。
<input type="text" value={locationSearch}
onChange={e => setLocationSearch(e.target.value)}/>
##テーブル内の行のマッピング
テーブルの行にいくつかの状態を追加する。
function App() {
const [locationSearch, setLocationSearch] = useState('Paris');
const [locations, setLocations] = useState(['Belfast', 'Dublin']);
これをビューにマップするにはtr、配列内のすべてのエントリ-に行が必要なので、文字通りmap:を実行する。
function App() {
const [locationSearch, setLocationSearch] = useState('Paris');
const [locations, setLocations] = useState(['Belfast', 'Dublin']);
return (
...
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
{locations.map(location =>
<tr><td>{location}</td></tr>
)}
</tbody>
</table>
...
);
}
map矢印関数内でネストされたJSXを返す方法と、JSXがネストされた{}式を埋め込むことができることに注意。
<tbody>
{locations.map(location =>
<tr><td>{location}</td></tr>
)}
</tbody>
このコードとビューのインターリーブは非常に便利で、最後に1つ変更する必要がある。要素のリストを使用する場合、Reactは、変更の追跡を効率的に実行できるように、エントリごとに一意のキーを必要する。後でロケーションIDを使用するが、今のところは、テーブル内のインデックスを使用するだけ。
<tbody>
{locations.map((location, index) =>
<tr key={index}><td>{location}</td></tr>
)}
</tbody>
##場所の追加
場所の追加をサポートするには、検索ボタンがクリックされたときにアクションをトリガーする必要がある。これはinputハンドラーに似ているが、onChangeイベントの代わりにonClickイベントを処理するようになった。変更ではなく、完全に新しい状態値を設定することによって、locations配列を変更することに注意する。これは、スプレッド演算子を使用すると簡単で、使用するlocationSearchを追加するためのデータとして、NOTinputコントロールを使用する。
<button onClick={() => setLocations([locationSearch, ...locations])}>Search</button>
ハンドラーをローカルとして分割します const
locationSearch空白の場合はボタンを無効にします
検索後に検索テキストを空白に設定する
const disableSearch = locationSearch.trim() === '';
const addLocation = () => {
setLocations([locationSearch, ...locations]);
setLocationSearch('');
};
return (
...
<button onClick={addLocation} disabled={disableSearch}>Search</button>
useStateフックから初期ダミーデータを削除できる。
locations状態の場合、初期データのタイプである空の配列を推測できないため、汎用パラメーターを明示的に指定することに注意する。string[]そのため、locationSearchそれは自動的にとして推論されstringにする。
function App() {
const [locationSearch, setLocationSearch] = useState('');
const [locations, setLocations] = useState<string[]>([]);
コンポーネントの分割
現在、このアプリには1つのコンポーネントしかない(App)。検索とテーブルをそれぞれのコンポーネントに分割する。
※ これは、アプリケーションをコンポーネントのツリーとして構造化する大規模なUIを構築するための一般的な方法。
場所検索コンポーネント
LocationSearchと呼ばれるファイルを作成し、LocationSearch.tsxの中に次のコードを配置するコンポーネントから始める。
ここでは、基本的に、ビューのこの検索部分をApp.tsxファイルから切り取って貼り付ける。FC定義を厳密に型指定するために、矢印関数に型を追加。
※ これは、機能コンポーネントを指定するReact型。
React最初の行にもインポートしたことに注意。React 17より前は、JSXを使用するすべてのファイルでこの行が必要。
これは、タグが最終的にへの呼び出しに変換されたためReact.createElement。
import React from "react";
import {FC, useState} from "react";
export const LocationSearch: FC = () => {
return (
<div>
<label>
Add Location
<input type="text" value={locationSearch}
onChange={e => setLocationSearch(e.target.value)}/>
</label>
<button onClick={addLocation} disabled={disableSearch}>Search</button>
</div>
);
}
次の私の状態について考える必要が-すべきlocationSearch文字列の国に属す。
AppかLocationSearch?この場合、入力テキストは一時的なものであり、この新しいコンポーネントに移動できる。
onClickハンドラは、しかし、変更locationsの状態を。locationsアプリの他の部分でも使用されるため、このコントロールの状態は使用したくないので、代わりにその状態をに残しAppする。この子コンポーネントからApp、検索ボタンがクリックされたときはいつでも通信する必要がある。これは、コールバックプロパティを定義することによって行う。
export const LocationSearch: FC = () => {
const [locationSearch, setLocationSearch] = useState('');
const disableSearch = locationSearch.trim() === '';
...
##コンポーネントへの小道具の追加
小道具は、JSXで属性として指定されたコンポーネントへの入力です。すべての小道具は、パラメータとして機能コンポーネントに渡されるオブジェクトにグループ化される。状態はコンポーネントに属するものと考えることができ、読み取り/書き込み。小道具は読み取り専用であり、コンポーネントに属していないため、渡される。関数をコールバック小道具として渡すことで、イベントハンドラー(この場合はのハンドラー)をアタッチできるonSearch。
...
<LocationSearch prop1={...} prop2={...}/>
...
TypeScriptでは、すべての小道具のインターフェイスを作成し、それを次の汎用パラメーターとして指定するFC。
interface LocationSearchProps {
onSearch: (search: string) => void;
}
export const LocationSearch: FC<LocationSearchProps> = (props) => {
...
コールバックはクリックMouseEventパラメータではなく文字列を受け取ることに注意。
これは、コンポーネントを持つことのもう1つの便利な点で、低レベルの詳細を非表示にして、よりクリーンなインターフェイスを構築できる。
汎用パラメーターを指定するFCと、props関数パラメーターが正しく入力され、パラメータリスト内で直接分解して、を消費することができる。onSearch。
export const LocationSearch: FC<LocationSearchProps> = (props) => {}
// Simplify using destructuring
export const LocationSearch: FC<LocationSearchProps> = ({onSearch}) => {}
コンポーネントにコールバックプロップが入ったので、検索ボタンがクリックされたときにそれを呼び出すことができる。
export const LocationSearch: FC<LocationSearchProps> = ({onSearch}) => {
const [locationSearch, setLocationSearch] = useState('');
const disableSearch = locationSearch.trim() === '';
const addLocation = () => {
onSearch(locationSearch); // Prop callback invoked - string passed
setLocationSearch('');
};
return (
...
<button className="btn btn-primary"
onClick={addLocation} disabled={disableSearch}>Search</button>
...
);
}
最後に、App.tsxこの新しいコンポーネントを使用するように更新できる。
...
import {LocationSearch} from "./LocationSearch";
function App() {
const [locations, setLocations] = useState<string[]>([]);
const addLocation = (location: string) => setLocations([location, ...locations]);
return (
<div className="container">
<h1>Weather App</h1>
<LocationSearch onSearch={addLocation}/>
<div>
<h2>Locations</h2>
<table className="table table-hover">
...
ロケーションテーブルコンポーネント
テーブルコンポーネントの場合も同様のプロセス。
- LocationTable.tsxファイルを作成
- テーブルビューをからApp.tsxファイル内の新しいコンポーネントに移動する
- 小道具のインターフェースを追加 (この場合は表示する場所)
import React, {FC} from "react";
interface LocationTableProps {
locations: string[];
}
export const LocationTable: FC<LocationTableProps> = ({locations}) =>
<div>
<h2>Locations</h2>
<table className="table table-hover">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
{locations.map((location, index) =>
<tr key={index}><td>{location}</td></tr>
)}
</tbody>
</table>
</div>;
view rawLocationTable.tsx hosted with ❤ by GitHub
import {LocationSearch} from "./LocationSearch";
import {LocationTable} from "./LocationTable";
function App() {
...
return (
<div className="container">
<h1>Weather App</h1>
<LocationSearch onSearch={addLocation}/>
<LocationTable locations={locations}/>
</div>
);
}
参考サイト
Reactチュートリアル—最初から天気アプリを作成する—パート1
Prettierの導入方法 フロントエンド開発で必須のコード整形ツール
VSCode + ESLint + Prettier + React + TypeScript (自分用メモ Fall, 2020)
.prettierrc
VS Codeのsettings.jsonの開き方
TypeScript+React用にprettierを導入する
TypeScript+React用にESLintを導入する