著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

React 開発では、Web アプリケーションプログラミングインターフェイス(API)は、シングルページアプリケーション(SPA)設計の不可欠な部分です。 APIは、アプリケーションがプログラムでサーバーと通信してユーザーにリアルタイムデータを提供し、ユーザーの変更を保存するための主要な方法です。 Reactアプリケーションでは、APIを使用して、ユーザー設定の読み込み、ユーザー情報の表示、構成またはセキュリティ情報の取得、およびアプリケーションの状態変更の保存を行います。

このチュートリアルでは、 useEffectuseState テスト目的でローカルAPIとしてJSONサーバーを使用して、サンプルアプリケーションで情報をフェッチして表示するためのフック。 コンポーネントが最初にマウントされたときに情報をロードし、APIを使用して顧客の入力を保存します。 また、ユーザーが変更を加えたときにデータを更新し、コンポーネントがアンマウントされたときにAPIリクエストを無視する方法を学びます。 このチュートリアルを終了するまでに、ReactアプリケーションをさまざまなAPIに接続し、リアルタイムデータを送受信できるようになります。

前提条件

ステップ1—プロジェクトとローカルAPIを作成する

このステップでは、テストデータソースとして使用する JSONサーバーを使用して、ローカルの RESTAPIを作成します。 後で、食料品のリストを表示し、リストにアイテムを追加するアプリケーションを作成します。 JSONサーバーがローカルAPIになり、作成するライブURLを提供します GETPOST リクエスト。 ローカルAPIを使用すると、自分または別のチームがライブAPIを開発している間に、コンポーネントのプロトタイプを作成してテストする機会があります。

このステップを完了すると、Reactアプリケーションに接続できるローカルのモックAPIを作成できるようになります。

多くのアジャイルチームでは、フロントエンドチームとAPIチームが並行して問題に取り組んでいます。 リモートAPIの開発中にフロントエンドアプリケーションを開発するために、完全なリモートAPIを待っている間に使用できるローカルバージョンを作成できます。

模擬ローカルAPIを作成する方法はたくさんあります。 Node または別の言語を使用して、単純なサーバーを作成できますが、最も簡単な方法は、JSONサーバーのNodeパッケージを使用することです。 このプロジェクトは、JSONファイルからローカルRESTAPIを作成します。

開始するには、 json-server:

  1. 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になります。

  1. nano db.json

次のキーを持つオブジェクトを追加します list および値の配列 id との鍵 item. これにより、食料品リストのアイテムが一覧表示されます。 キー list 最終的にエンドポイントが /list:

api-tutorial / db.json
{
  "list": [
    { "id": 1, "item": "bread" },
    { "id": 2, "item": "grapes" }
  ]
}

このスニペットでは、ハードコーディングされています breadgrapes 食料品リストの出発点として。

ファイルを保存して閉じます。 APIサーバーを実行するには、次を使用します json-server 引数を指定したコマンドラインからAPI構成ファイルへ。 package.jsonにスクリプトとして追加します。

開ける package.json:

  1. nano package.json

次に、APIを実行するためのスクリプトを追加します。 さらに、 delay 財産。 これにより、応答が抑制され、APIリクエストとAPIレスポンスの間に遅延が生じます。 これにより、サーバーの応答を待機しているときにアプリケーションがどのように動作するかについての洞察が得られます。 追加する delay1500 ミリ秒。 最後に、ポートでAPIを実行します 3333 を使用して -p オプションと競合しないように create-react-app スクリプトを実行します。

api-tutorial / package.json
{
  "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サーバーを起動します。

  1. 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 新しいアイテム。 新しいターミナルウィンドウまたはタブで、 curlPOST タイプの新しいアイテム application/json:

  1. 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アクションを使用してそれらの値をフェッチまたは更新する方法を学習しました。 GETPOST. 次のステップでは、APIからデータをフェッチし、アプリケーションに表示するサービスを作成します。

ステップ2—APIからデータを取得する useEffect

このステップでは、を使用して食料品のリストを取得します useEffect 針。 別のディレクトリでAPIを使用するサービスを作成し、Reactコンポーネントでそのサービスを呼び出します。 サービスを呼び出した後、データを保存します useState コンポーネントに結果をフックして表示します。

この手順を完了すると、FetchメソッドuseEffect 針。 結果を保存して表示することもできます。

APIが機能するようになったので、データとコンポーネントをフェッチして情報を表示するサービスが必要です。 サービスを作成することから始めます。 Reactコンポーネント内で直接データをフェッチできますが、データ取得機能を表示コンポーネントから分離しておくと、プロジェクトの参照と更新が簡単になります。 これにより、コンポーネント間でメソッドを再利用したり、テストをモックインしたり、エンドポイントが変更されたときにURLを更新したりできます。

と呼ばれるディレクトリを作成します servicessrc ディレクトリ:

  1. mkdir src/services

次に、というファイルを開きます list.js テキストエディタで:

  1. nano src/services/list.js

このファイルは、 /list 終点。 fetch関数を使用してデータを取得する関数を追加します。

api-tutorial / src / services / list
export function getList() {
  return fetch('http://localhost:3333/list')
    .then(data => data.json())
}

この関数の唯一の目的は、データにアクセスし、を使用して応答をJSONに変換することです。 data.json() 方法。 GET はデフォルトのアクションであるため、他のパラメータは必要ありません。

に加えて fetch Axios などの他の一般的なライブラリがあり、直感的なインターフェイスを提供し、デフォルトのヘッダーを追加したり、サービスで他のアクションを実行したりできます。 しかし fetch ほとんどのリクエストで機能します。

ファイルを保存して閉じます。 次に、開く App.css 最小限のスタイリングを追加します。

  1. nano src/components/App/App.css

次のクラスを追加します wrapper CSSを次のように置き換えることにより、少量のパディングを行います。

api-tutorial / src / components / App / App.css
.wrapper {
    padding: 15px;
}

ファイルを保存して閉じます。 次に、データを取得してアプリケーションに表示するためのコードを追加する必要があります。

開ける App.js:

  1. nano src/components/App/App.js

機能コンポーネントでは、 useEffect コンポーネントがロードされたとき、または一部の情報が変更されたときにデータをフェッチするためのフック。 詳細については、 useEffect フックして、非同期データの読み込み、遅延読み込み、Reactを使用したコード分割の処理方法を確認してください。 また、結果を保存する必要があります useState 針。

輸入 useEffectuseState、次にという変数を作成します list と呼ばれるセッター setList を使用してサービスからフェッチしたデータを保持する useState 針:

api-tutorial / src / components / App / App.js
import React, { useEffect, useState } from 'react';
import './App.css';

function App() {
  const [list, setList] = useState([]);
  return(
    <>
    </>
  )
}

export default App;

次に、サービスをインポートしてから、内部のサービスを呼び出します useEffect 針。 を更新します listsetList コンポーネントがマウントされている場合。 データを設定する前にコンポーネントがマウントされているかどうかを確認する必要がある理由を理解するには、非同期データの読み込み、遅延読み込み、Reactによるコード分割の処理方法のステップ2 —マウントされていないコンポーネントのエラーの防止を参照してください。

現在、ページの読み込み時にエフェクトを実行しているのは1回だけなので、依存関係の配列は空になります。 次のステップでは、さまざまなページアクションに基づいてエフェクトをトリガーし、常に最新の情報を入手できるようにします。

次の強調表示されたコードを追加します。

api-tutorial / src / components / App / App.js

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 を使用してアイテムをループし、リストに表示します。

api-tutorial / src / components / App / App
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:

  1. nano src/services/list.js

ファイル内に、 item 引数として、を使用してデータを送信します POST APIへのメソッド。 以前と同様に、FetchAPIを使用できます。 今回は、より多くの情報が必要になります。 2番目の引数としてオプションのオブジェクトを追加します。 メソッドを含める—POST-ヘッダーとともに Content-Typeapplication/json. 最後に、新しいオブジェクトを body. 必ずを使用してオブジェクトを文字列に変換してください JSON.stringify.

応答を受け取ったら、値をJSONに変換します。

tutorial / src / services / list.js
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:

  1. nano src/components/App/App.js

追加する form を囲む要素 input と送信 button:

api-tutorial / src / components / App / App.js

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 針:

api-tutorial / src / components / App / App.js
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;

状態ハンドラーを作成した後、の値を設定します inputitemInput を渡すことによって値を更新します event.target.valuesetItemInput を使用して機能する onChange イベントハンドラ。

これで、ユーザーは新しいリストアイテムをフォームに入力できます。 次に、フォームをサービスに接続します。

と呼ばれる関数を作成します handleSubmit. handleSubmit 引数としてイベントを取り、呼び出します event.preventDefault() フォームによるブラウザの更新を停止します。

輸入 setItem サービスから、電話 setItem とともに itemInput 内部の値 handleSubmit 関数。 接続 handleSubmit フォームに渡すことでフォームに onSubmit イベントハンドラー:

api-tutorial / src / components / App / App.js
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 が解決し、入力をクリアして、アラートメッセージを設定します。

api-tutorial / src / components / App / App.js
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 からの変更 falsetrue、ただし、次の場合にも実行されます alert からの変更 truefalse. アラートが表示された場合にのみ非表示にしたいので、エフェクト内に条件を追加して実行のみにします setTimeout もしも alerttrue:

api-tutorial / src / components / App / App.js
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 真実であり、データを既にフェッチしていることを示します。 alertfalse-データがすでに更新されていることを示します。 必ず追加してください alertlist トリガーの配列に:

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 関数:

api-tutorial / src / components / App / App.js
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アプリを実行しているターミナルでエラーが発生します。

Error
Assignments 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 針。 次に、の各使用法を変換します mountedmounted.current:

api-tutorial / src / components / App / App.js
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.currenttrue エフェクトの開始時。 そうすれば、次のようにのみ設定されることを確認できます。 false コンポーネントがマウント解除されたとき。

ファイルを保存して閉じます。 ブラウザが更新されると、新しいリストアイテムを保存できるようになります。

注:誤ってAPIを複数回再実行することは一般的な問題です。 コンポーネントを削除してから再マウントするたびに、元のデータフェッチをすべて再実行します。 これを回避するには、特にデータ量が多いまたは遅いAPIのキャッシュ方法を検討してください。 サービス呼び出しのメモ化から、サービスワーカーとのキャッシュ、カスタムフックまで、あらゆるものを使用できます。 useSWRreactquery など、サービス呼び出しをキャッシュするための一般的なカスタムフックがいくつかあります。

どのアプローチを使用する場合でも、最新のデータをフェッチしたい場合があるため、キャッシュを無効にする方法を必ず検討してください。

このステップでは、APIにデータを送信しました。 データが送信されたときにユーザーを更新する方法と、リストデータの更新をトリガーする方法を学習しました。 また、を使用して、マウントされていないコンポーネントにデータを設定することを回避しました useRef 複数のサービスで使用できるように、コンポーネントのステータスを保存するためのフック。

結論

APIを使用すると、多くの便利なサービスに接続できます。 ユーザーがブラウザを閉じたり、アプリケーションの使用を停止したりした後でも、データを保存および取得できます。 適切に編成されたコードを使用すると、サービスをコンポーネントから分離できるため、コンポーネントは、データの発信元を知らなくてもデータのレンダリングに集中できます。 Web APIは、ブラウザセッションまたはストレージの機能をはるかに超えてアプリケーションを拡張します。 彼らはあなたのアプリケーションをウェブ技術の全世界に開放します。

Reactチュートリアルをもっと読みたい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。