著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
React 開発では、Web アプリケーションプログラミングインターフェイス(API)は、シングルページアプリケーション(SPA)設計の不可欠な部分です。 APIは、アプリケーションがプログラムでサーバーと通信してユーザーにリアルタイムデータを提供し、ユーザーの変更を保存するための主要な方法です。 Reactアプリケーションでは、APIを使用して、ユーザー設定の読み込み、ユーザー情報の表示、構成またはセキュリティ情報の取得、およびアプリケーションの状態変更の保存を行います。
このチュートリアルでは、 useEffect
と useState
テスト目的でローカルAPIとしてJSONサーバーを使用して、サンプルアプリケーションで情報をフェッチして表示するためのフック。 コンポーネントが最初にマウントされたときに情報をロードし、APIを使用して顧客の入力を保存します。 また、ユーザーが変更を加えたときにデータを更新し、コンポーネントがアンマウントされたときにAPIリクエストを無視する方法を学びます。 このチュートリアルを終了するまでに、ReactアプリケーションをさまざまなAPIに接続し、リアルタイムデータを送受信できるようになります。
前提条件
-
Node.jsを実行する開発環境が必要になります。 このチュートリアルは、Node.jsバージョン10.22.0およびnpmバージョン6.14.6でテストされました。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはのPPAを使用したインストール]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。
-
Create React App でセットアップされたReact開発環境で、不要なボイラープレートが削除されています。 これを設定するには、ステップ1 —Reactクラスコンポーネントの状態を管理する方法のチュートリアルの空のプロジェクトを作成します。 このチュートリアルでは、
api-tutorial
プロジェクト名として。 -
このチュートリアルでは、Reactコンポーネントとフックを使用します。
useState
とuseEffect
フック。 コンポーネントとフックについては、チュートリアルReactコンポーネントのフックで状態を管理する方法とReactで非同期データの読み込み、遅延読み込み、コード分割を処理する方法で学ぶことができます。 -
また、JavaScriptとHTMLの基本的な知識も必要です。これは、HTMLシリーズでWebサイトを構築する方法およびJavaScriptでコーディングする方法にあります。 CSSの基本的な知識も役立ちます。これは、 Mozilla DeveloperNetworkで見つけることができます。
ステップ1—プロジェクトとローカルAPIを作成する
このステップでは、テストデータソースとして使用する JSONサーバーを使用して、ローカルの RESTAPIを作成します。 後で、食料品のリストを表示し、リストにアイテムを追加するアプリケーションを作成します。 JSONサーバーがローカルAPIになり、作成するライブURLを提供します GET
と POST
リクエスト。 ローカルAPIを使用すると、自分または別のチームがライブAPIを開発している間に、コンポーネントのプロトタイプを作成してテストする機会があります。
このステップを完了すると、Reactアプリケーションに接続できるローカルのモックAPIを作成できるようになります。
多くのアジャイルチームでは、フロントエンドチームとAPIチームが並行して問題に取り組んでいます。 リモートAPIの開発中にフロントエンドアプリケーションを開発するために、完全なリモートAPIを待っている間に使用できるローカルバージョンを作成できます。
模擬ローカルAPIを作成する方法はたくさんあります。 Node または別の言語を使用して、単純なサーバーを作成できますが、最も簡単な方法は、JSONサーバーのNodeパッケージを使用することです。 このプロジェクトは、JSONファイルからローカルRESTAPIを作成します。
開始するには、 json-server
:
- npm install --save-dev json-server
インストールが完了すると、成功メッセージが表示されます。
Output+ json-server@0.16.1
added 108 packages from 40 contributors and audited 1723 packages in 14.505s
73 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
json-server
JavaScriptオブジェクトに基づいてAPIを作成します。 キーはURLパスであり、値は応答として返されます。 JavaScriptオブジェクトをローカルに保存し、ソース管理にコミットします。
というファイルを開きます db.json
アプリケーションのルートにあります。 これは、APIから要求する情報を格納するJSONになります。
- nano db.json
次のキーを持つオブジェクトを追加します list
および値の配列 id
との鍵 item
. これにより、食料品リストのアイテムが一覧表示されます。 キー list
最終的にエンドポイントが /list
:
{
"list": [
{ "id": 1, "item": "bread" },
{ "id": 2, "item": "grapes" }
]
}
このスニペットでは、ハードコーディングされています bread
と grapes
食料品リストの出発点として。
ファイルを保存して閉じます。 APIサーバーを実行するには、次を使用します json-server
引数を指定したコマンドラインからAPI構成ファイルへ。 package.jsonにスクリプトとして追加します。
開ける package.json
:
- nano package.json
次に、APIを実行するためのスクリプトを追加します。 さらに、 delay
財産。 これにより、応答が抑制され、APIリクエストとAPIレスポンスの間に遅延が生じます。 これにより、サーバーの応答を待機しているときにアプリケーションがどのように動作するかについての洞察が得られます。 追加する delay
の 1500
ミリ秒。 最後に、ポートでAPIを実行します 3333
を使用して -p
オプションと競合しないように create-react-app
スクリプトを実行します。
{
"name": "do-14-api",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.3"
},
"scripts": {
"api": "json-server db.json -p 3333 --delay 1500",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"json-server": "^0.16.1"
}
}
ファイルを保存して閉じます。 新しいターミナルまたはタブで、次のコマンドを使用してAPIサーバーを起動します。
- npm run api
チュートリアルの残りの間、これを実行し続けます。
コマンドを実行すると、APIリソースを一覧表示する出力が表示されます。
Output> json-server db.json -p 3333
\{^_^}/ hi!
Loading db.json
Done
Resources
http://localhost:3333/list
Home
http://localhost:3333
Type s + enter at any time to create a snapshot of the database
http:// localhost:3333 / list を開くと、ライブAPIが見つかります。
ブラウザでエンドポイントを開くと、 GET
方法。 しかし json-server
に限定されません GET
方法。 他の多くのRESTメソッドも実行できます。 たとえば、次のことができます POST
新しいアイテム。 新しいターミナルウィンドウまたはタブで、 curl
に POST
タイプの新しいアイテム application/json
:
- curl -d '{"item":"rice"}' -H 'Content-Type: application/json' -X POST http://localhost:3333/list
送信する前に、コンテンツを文字列化する必要があることに注意してください。 実行後 curl
コマンドを実行すると、成功メッセージが表示されます。
Output{
"item": "rice",
"id": 3
}
ブラウザを更新すると、新しいアイテムが表示されます。
The POST
リクエストも更新されます db.json
ファイル。 アプリケーションで作業するときに、構造化されていないコンテンツや役に立たないコンテンツを誤って保存することへの障壁がないため、変更に注意してください。 バージョン管理にコミットする前に、必ず変更を確認してください。
このステップでは、ローカルAPIを作成しました。 デフォルト値を使用して静的ファイルを作成する方法と、次のようなRESTfulアクションを使用してそれらの値をフェッチまたは更新する方法を学習しました。 GET
と POST
. 次のステップでは、APIからデータをフェッチし、アプリケーションに表示するサービスを作成します。
ステップ2—APIからデータを取得する useEffect
このステップでは、を使用して食料品のリストを取得します useEffect
針。 別のディレクトリでAPIを使用するサービスを作成し、Reactコンポーネントでそのサービスを呼び出します。 サービスを呼び出した後、データを保存します useState
コンポーネントに結果をフックして表示します。
この手順を完了すると、Fetchメソッドと useEffect
針。 結果を保存して表示することもできます。
APIが機能するようになったので、データとコンポーネントをフェッチして情報を表示するサービスが必要です。 サービスを作成することから始めます。 Reactコンポーネント内で直接データをフェッチできますが、データ取得機能を表示コンポーネントから分離しておくと、プロジェクトの参照と更新が簡単になります。 これにより、コンポーネント間でメソッドを再利用したり、テストをモックインしたり、エンドポイントが変更されたときにURLを更新したりできます。
と呼ばれるディレクトリを作成します services
中 src
ディレクトリ:
- mkdir src/services
次に、というファイルを開きます list.js
テキストエディタで:
- nano src/services/list.js
このファイルは、 /list
終点。 fetch関数を使用してデータを取得する関数を追加します。
export function getList() {
return fetch('http://localhost:3333/list')
.then(data => data.json())
}
この関数の唯一の目的は、データにアクセスし、を使用して応答をJSONに変換することです。 data.json()
方法。 GET
はデフォルトのアクションであるため、他のパラメータは必要ありません。
に加えて fetch
、 Axios などの他の一般的なライブラリがあり、直感的なインターフェイスを提供し、デフォルトのヘッダーを追加したり、サービスで他のアクションを実行したりできます。 しかし fetch
ほとんどのリクエストで機能します。
ファイルを保存して閉じます。 次に、開く App.css
最小限のスタイリングを追加します。
- nano src/components/App/App.css
次のクラスを追加します wrapper
CSSを次のように置き換えることにより、少量のパディングを行います。
.wrapper {
padding: 15px;
}
ファイルを保存して閉じます。 次に、データを取得してアプリケーションに表示するためのコードを追加する必要があります。
開ける App.js
:
- nano src/components/App/App.js
機能コンポーネントでは、 useEffect
コンポーネントがロードされたとき、または一部の情報が変更されたときにデータをフェッチするためのフック。 詳細については、 useEffect
フックして、非同期データの読み込み、遅延読み込み、Reactを使用したコード分割の処理方法を確認してください。 また、結果を保存する必要があります useState
針。
輸入 useEffect
と useState
、次にという変数を作成します list
と呼ばれるセッター setList
を使用してサービスからフェッチしたデータを保持する useState
針:
import React, { useEffect, useState } from 'react';
import './App.css';
function App() {
const [list, setList] = useState([]);
return(
<>
</>
)
}
export default App;
次に、サービスをインポートしてから、内部のサービスを呼び出します useEffect
針。 を更新します list
と setList
コンポーネントがマウントされている場合。 データを設定する前にコンポーネントがマウントされているかどうかを確認する必要がある理由を理解するには、非同期データの読み込み、遅延読み込み、Reactによるコード分割の処理方法のステップ2 —マウントされていないコンポーネントのエラーの防止を参照してください。 。
現在、ページの読み込み時にエフェクトを実行しているのは1回だけなので、依存関係の配列は空になります。 次のステップでは、さまざまなページアクションに基づいてエフェクトをトリガーし、常に最新の情報を入手できるようにします。
次の強調表示されたコードを追加します。
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList } from '../../services/list';
function App() {
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [])
return(
<>
</>
)
}
export default App;
最後に、 .map を使用してアイテムをループし、リストに表示します。
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList } from '../../services/list';
function App() {
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [])
return(
<div className="wrapper">
<h1>My Grocery List</h1>
<ul>
{list.map(item => <li key={item.item}>{item.item}</li>)}
</ul>
</div>
)
}
export default App;
ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、アイテムのリストが表示されます。
このステップでは、APIからデータを取得するサービスを設定します。 を使用してサービスを呼び出す方法を学びました useEffect
フックとページ上のデータの設定方法。 JSX内にもデータを表示しました。
次のステップでは、を使用してAPIにデータを送信します POST
応答を使用して、アクションが成功したことをユーザーに警告します。
ステップ3—APIにデータを送信する
このステップでは、FetchAPIと POST
方法。 Webフォームを使用してデータを送信するコンポーネントを作成します。 onSubmit
イベントハンドラーであり、アクションが完了すると成功メッセージを表示します。
この手順を完了すると、APIに情報を送信できるようになり、リクエストが解決したときにユーザーにアラートを送信できるようになります。
サービスへのデータの送信
食料品のリストを表示するアプリケーションがありますが、コンテンツも保存できない限り、あまり便利な食料品アプリではありません。 あなたはするサービスを作成する必要があります POST
APIの新しいアイテム。
開く src/services/list.js
:
- nano src/services/list.js
ファイル内に、 item
引数として、を使用してデータを送信します POST
APIへのメソッド。 以前と同様に、FetchAPIを使用できます。 今回は、より多くの情報が必要になります。 2番目の引数としてオプションのオブジェクトを追加します。 メソッドを含める—POST
-ヘッダーとともに Content-Type
に application/json
. 最後に、新しいオブジェクトを body
. 必ずを使用してオブジェクトを文字列に変換してください JSON.stringify
.
応答を受け取ったら、値をJSONに変換します。
export function getList() {
return fetch('http://localhost:3333/list')
.then(data => data.json())
}
export function setItem(item) {
return fetch('http://localhost:3333/list', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ item })
})
.then(data => data.json())
}
ファイルを保存して閉じます。
注:実稼働アプリケーションでは、エラー処理とチェックを追加する必要があります。 たとえば、エンドポイントのスペルを間違えた場合でも、 404
応答と data.json()
メソッドは空のオブジェクトを返します。 この問題を解決するには、応答をJSONに変換する代わりに、 data.ok
財産。 偽物の場合は、エラーをスローしてから、 .catch
コンポーネントのメソッドを使用して、ユーザーに失敗メッセージを表示します。
サービスを作成したので、コンポーネント内でサービスを利用する必要があります。
開ける App.js
:
- nano src/components/App/App.js
追加する form
を囲む要素 input
と送信 button
:
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList } from '../../services/list';
function App() {
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [])
return(
<div className="wrapper">
<h1>My Grocery List</h1>
<ul>
{list.map(item => <li key={item.item}>{item.item}</li>)}
</ul>
<form>
<label>
<p>New Item</p>
<input type="text" />
</label>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
必ず囲んでください input
とともに label
スクリーンリーダーでフォームにアクセスできるようにします。 追加するのも良い習慣です type="submit"
に button
フォームを送信することが役割であることは明らかです。
ファイルを保存します。 これを行うと、ブラウザが更新され、フォームが見つかります。
次に、変換します input
制御コンポーネントに。 ユーザーが新しいリストアイテムを正常に送信した後にフィールドをクリアできるように、制御されたコンポーネントが必要になります。
まず、新しい状態ハンドラーを作成して、入力情報を保持および設定します。 useState
針:
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList } from '../../services/list';
function App() {
const [itemInput, setItemInput] = useState('');
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [])
return(
<div className="wrapper">
<h1>My Grocery List</h1>
<ul>
{list.map(item => <li key={item.item}>{item.item}</li>)}
</ul>
<form>
<label>
<p>New Item</p>
<input type="text" onChange={event => setItemInput(event.target.value)} value={itemInput} />
</label>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
状態ハンドラーを作成した後、の値を設定します input
に itemInput
を渡すことによって値を更新します event.target.value
に setItemInput
を使用して機能する onChange
イベントハンドラ。
これで、ユーザーは新しいリストアイテムをフォームに入力できます。 次に、フォームをサービスに接続します。
と呼ばれる関数を作成します handleSubmit
. handleSubmit
引数としてイベントを取り、呼び出します event.preventDefault()
フォームによるブラウザの更新を停止します。
輸入 setItem
サービスから、電話 setItem
とともに itemInput
内部の値 handleSubmit
関数。 接続 handleSubmit
フォームに渡すことでフォームに onSubmit
イベントハンドラー:
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList, setItem } from '../../services/list';
function App() {
const [itemInput, setItemInput] = useState('');
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [])
const handleSubmit = (e) => {
e.preventDefault();
setItem(itemInput)
};
return(
<div className="wrapper">
<h1>My Grocery List</h1>
<ul>
{list.map(item => <li key={item.item}>{item.item}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<label>
<p>New Item</p>
<input type="text" onChange={event => setItemInput(event.target.value)} value={itemInput} />
</label>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
ファイルを保存します。 そうすると、値を送信できるようになります。 [ネットワーク]タブに正常な応答が表示されることに注意してください。 しかし、リストは更新されず、入力はクリアされません。
成功メッセージの表示
アクションが成功したことをユーザーに示すことは、常に良い習慣です。 そうしないと、ユーザーは値を複数回再送信しようとしたり、アクションが失敗したと思ってアプリケーションを終了したりする可能性があります。
これを行うには、ステートフル変数とセッター関数を次のように作成します。 useState
ユーザーにアラートメッセージを表示するかどうかを示します。 もしも alert
trueの場合、表示します <h2>
送信成功というメッセージのタグを付けます。
いつ setItem
promise が解決し、入力をクリアして、アラートメッセージを設定します。
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList, setItem } from '../../services/list';
function App() {
const [alert, setAlert] = useState(false);
const [itemInput, setItemInput] = useState('');
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [])
const handleSubmit = (e) => {
e.preventDefault();
setItem(itemInput)
.then(() => {
setItemInput('');
setAlert(true);
})
};
return(
<div className="wrapper">
<h1>My Grocery List</h1>
<ul>
{list.map(item => <li key={item.item}>{item.item}</li>)}
</ul>
{alert && <h2> Submit Successful</h2>}
<form onSubmit={handleSubmit}>
<label>
<p>New Item</p>
<input type="text" onChange={event => setItemInput(event.target.value)} value={itemInput} />
</label>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
ファイルを保存します。 これを行うと、ページが更新され、APIリクエストが解決された後に成功メッセージが表示されます。
追加できる最適化は他にもたくさんあります。 たとえば、アクティブなリクエストがある間はフォーム入力を無効にしたい場合があります。 フォーム要素の無効化について詳しくは、Reactでフォームを作成する方法をご覧ください。
これで、結果が成功したことをユーザーに警告しましたが、警告メッセージは消えず、リストは更新されません。 これを修正するには、アラートを非表示にすることから始めます。 この場合、1秒などの短い期間の後に情報を非表示にする必要があります。 あなたは使用することができます setTimeout
呼び出す関数 setAlert(false)
、しかしあなたはそれを包む必要があるでしょう useEffect
すべてのコンポーネントレンダリングで実行されないようにするため。
の中に App.js
新しい効果を作成し、 alert
トリガーの配列に。 これにより、エフェクトはいつでも実行されます alert
変更します。 これは次の場合に実行されることに注意してください alert
からの変更 false
に true
、ただし、次の場合にも実行されます alert
からの変更 true
に false
. アラートが表示された場合にのみ非表示にしたいので、エフェクト内に条件を追加して実行のみにします setTimeout
もしも alert
は true
:
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList, setItem } from '../../services/list';
function App() {
const [alert, setAlert] = useState(false);
const [itemInput, setItemInput] = useState('');
const [list, setList] = useState([]);
...
useEffect(() => {
if(alert) {
setTimeout(() => {
setAlert(false);
}, 1000)
}
}, [alert])
const handleSubmit = (e) => {
e.preventDefault();
setItem(itemInput)
.then(() => {
setItemInput('');
setAlert(true);
})
};
return(
<div className="wrapper">
...
</div>
)
}
export default App;
を実行します setTimeout
後の機能 1000
ユーザーが変更を読み取る時間を確保するためのミリ秒。
ファイルを保存します。 これで、いつでも実行されるエフェクトがあります alert
変更します。 アクティブなアラートがある場合、1秒後にアラートを閉じるタイムアウト機能を開始します。
取得したデータを更新する
次に、古いデータのリストを更新する方法が必要です。 これを行うには、新しいトリガーをに追加できます useEffect
フックして再実行します getList
リクエスト。 最も関連性の高いデータを確保するには、リモートデータに変更があったときにいつでも更新されるトリガーが必要です。 幸いなことに、あなたは再利用することができます alert
ユーザーがデータを更新するたびに実行されることがわかっているため、別のデータ更新をトリガーする状態。 以前と同様に、エフェクトが毎回実行されるという事実を計画する必要があります alert
アラートメッセージが消えるときを含む変更。
今回は、ページの読み込み時にエフェクトもデータをフェッチする必要があります。 次の場合、データフェッチの前に関数を終了する条件を作成します list.length
真実であり、データを既にフェッチしていることを示します。 alert
は false
-データがすでに更新されていることを示します。 必ず追加してください alert
と list
トリガーの配列に:
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList, setItem } from '../../services/list';
function App() {
const [alert, setAlert] = useState(false);
const [itemInput, setItemInput] = useState('');
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
if(list.length && !alert) {
return;
}
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [alert, list])
...
return(
<div className="wrapper">
...
</div>
)
}
export default App;
ファイルを保存します。 これを行うと、新しいアイテムを送信した後にデータが更新されます。
この場合、 alert
に直接関係していません list
州。 ただし、古いデータを無効にするイベントと同時に発生するため、これを使用してデータを更新できます。
マウントされていないコンポーネントの更新の防止
考慮する必要がある最後の問題は、マウントされていないコンポーネントに状態を設定しないようにすることです。 あなたは問題の解決策を持っています let mounted = true
データをフェッチする効果がありますが、ソリューションは handleSubmit
、効果ではないので。 アンマウント時に値をfalseに設定する関数を返すことはできません。 さらに、すべての関数に同じチェックを追加するのは非効率的です。
この問題を解決するために、持ち上げることによって複数の関数によって使用される共有変数を作成できます mounted
の外へ useEffect
コンポーネントのレベルに引っ掛けて保持します。 この関数を使用して、値を次のように設定します false
の終わりに useEffect
.
中身 App.js
、 宣言する mounted
関数の開始時。 次に、他の非同期機能でデータを設定する前に、コンポーネントがマウントされているかどうかを確認してください。 必ず削除してください mounted
内部の宣言 useEffect
関数:
import React, { useEffect, useState } from 'react';
import './App.css';
import { getList, setItem } from '../../services/list';
function App() {
const [alert, setAlert] = useState(false);
const [itemInput, setItemInput] = useState('');
const [list, setList] = useState([]);
let mounted = true;
useEffect(() => {
if(list.length && !alert) {
return;
}
getList()
.then(items => {
if(mounted) {
setList(items)
}
})
return () => mounted = false;
}, [alert, list])
useEffect(() => {
if(alert) {
setTimeout(() => {
if(mounted) {
setAlert(false);
}
}, 1000)
}
}, [alert])
const handleSubmit = (e) => {
e.preventDefault();
setItem(itemInput)
.then(() => {
if(mounted) {
setItemInput('');
setAlert(true);
}
})
};
return(
<div className="wrapper">
...
</div>
)
}
export default App;
変更を加えると、Reactアプリを実行しているターミナルでエラーが発生します。
ErrorAssignments to the 'mounted' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect react-hooks/exhaustive-deps
Reactは、変数が安定していないことを警告しています。 再レンダリングがあるときはいつでも、変数を再計算します。 通常、これにより最新の情報が保証されます。 この場合、永続化するためにその変数に依存しています。
解決策は、useRefと呼ばれる別のフックです。 The useRef
フックは、コンポーネントの存続期間中、変数を保持します。 唯一の秘訣は、使用するために必要な値を取得することです。 .current
財産。
中身 App.js
、 変換 mounted
を使用して参照に useRef
針。 次に、の各使用法を変換します mounted
に mounted.current
:
import React, { useEffect, useRef, useState } from 'react';
import './App.css';
import { getList, setItem } from '../../services/list';
function App() {
const [alert, setAlert] = useState(false);
const [itemInput, setItemInput] = useState('');
const [list, setList] = useState([]);
const mounted = useRef(true);
useEffect(() => {
mounted.current = true;
if(list.length && !alert) {
return;
}
getList()
.then(items => {
if(mounted.current) {
setList(items)
}
})
return () => mounted.current = false;
}, [alert, list])
useEffect(() => {
if(alert) {
setTimeout(() => {
if(mounted.current) {
setAlert(false);
}
}, 1000)
}
}, [alert])
const handleSubmit = (e) => {
e.preventDefault();
setItem(itemInput)
.then(() => {
if(mounted.current) {
setItemInput('');
setAlert(true);
}
})
};
return(
<div className="wrapper">
...
</div>
)
}
export default App;
また、クリーンアップ関数の変数の設定には注意が必要です。 useEffect
. クリーンアップ機能は、エフェクトが再実行される前に常に実行されます。 つまり、クリーンアップ機能 () => mounted.current = false
毎回実行されます alert
また list
変化する。 誤った結果を避けるために、必ず更新してください mounted.current
に true
エフェクトの開始時。 そうすれば、次のようにのみ設定されることを確認できます。 false
コンポーネントがマウント解除されたとき。
ファイルを保存して閉じます。 ブラウザが更新されると、新しいリストアイテムを保存できるようになります。
注:誤ってAPIを複数回再実行することは一般的な問題です。 コンポーネントを削除してから再マウントするたびに、元のデータフェッチをすべて再実行します。 これを回避するには、特にデータ量が多いまたは遅いAPIのキャッシュ方法を検討してください。 サービス呼び出しのメモ化から、サービスワーカーとのキャッシュ、カスタムフックまで、あらゆるものを使用できます。 useSWRやreactquery など、サービス呼び出しをキャッシュするための一般的なカスタムフックがいくつかあります。
どのアプローチを使用する場合でも、最新のデータをフェッチしたい場合があるため、キャッシュを無効にする方法を必ず検討してください。
このステップでは、APIにデータを送信しました。 データが送信されたときにユーザーを更新する方法と、リストデータの更新をトリガーする方法を学習しました。 また、を使用して、マウントされていないコンポーネントにデータを設定することを回避しました useRef
複数のサービスで使用できるように、コンポーネントのステータスを保存するためのフック。
結論
APIを使用すると、多くの便利なサービスに接続できます。 ユーザーがブラウザを閉じたり、アプリケーションの使用を停止したりした後でも、データを保存および取得できます。 適切に編成されたコードを使用すると、サービスをコンポーネントから分離できるため、コンポーネントは、データの発信元を知らなくてもデータのレンダリングに集中できます。 Web APIは、ブラウザセッションまたはストレージの機能をはるかに超えてアプリケーションを拡張します。 彼らはあなたのアプリケーションをウェブ技術の全世界に開放します。
Reactチュートリアルをもっと読みたい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。