Axiosを使用してReactでライブ検索機能を作成する
Axiosは、JavaScriptアプリにAjaxリクエストを簡単に実装できる強力なHTTPクライアントです。 React here でAxiosを使用するための基本について説明したので、AxiosまたはAxios + Reactがまったく新しい場合は、最初にそれを読むことができます。
このチュートリアルでは、Axiosを使用してReactアプリ内にライブ検索機能を構築します。 私たちのアプリでは、themoviedb.orgのAPIを使用して簡単な映画検索を行うことができます。
このチュートリアルは3つのセクションに分かれています。
- パート1:ReactwithAxiosでライブ検索を機能させる方法
- パート2:不要なリクエストの防止
- パート3:HTTPリクエストと応答のキャッシュ
アプリの初期化
このチュートリアルは、Reactの使用経験があることを前提としているため、貴重な時間を節約するために初期化の手順をスキップします。 お気に入りのボイラープレートを使用できます。このチュートリアルでは、 Create ReactAppを使用してアプリを初期化します。
アプリが初期化されたら、axios
を追加しましょう。
$ yarn add axios or npm i axios
次に、以下のコードをApp
コンポーネントにコピーします。
import React, { Component } from 'react';
import axios from 'axios';
import Movies from './Movies';
class App extends Component {
state = {
movies: null,
loading: false,
value: ''
};
search = async val => {
this.setState({ loading: true });
const res = await axios(
`https://api.themoviedb.org/3/search/movie?query=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b`
);
const movies = await res.data.results;
this.setState({ movies, loading: false });
};
onChangeHandler = async e => {
this.search(e.target.value);
this.setState({ value: e.target.value });
};
get renderMovies() {
let movies = <h1>There's no movies</h1>;
if (this.state.movies) {
movies = <Movies list={this.state.movies} />;
}
return movies;
}
render() {
return (
<div>
<input
value={this.state.value}
onChange={e => this.onChangeHandler(e)}
placeholder="Type something to search"
/>
{this.renderMovies}
</div>
);
}
}
export default App;
注:Movies
は単なるプレゼンテーション/ダムコンポーネントであり、提供するデータをレンダリングするだけです。 それは私たちのデータには影響しません。
入力コンポーネント
したがって、何かを入力するときにonChangeHandler
メソッドを呼び出す制御されたinput
要素があります。 onChangeHandler
は、state
のvalueプロパティを変更し、search
メソッドを呼び出して、入力の値を引数として指定します。
探す
上から次のコードを取り出します。
search = async val => {
this.setState({ loading: true });
const res = await axios(
`https://api.themoviedb.org/3/search/movie?query=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b`
);
const movies = await res.data.results;
this.setState({ movies, loading: false });
};
search
メソッドでは、必要な映画を取得するためにAPIにGET
リクエストを送信しています。 結果が得られたら、setState
を介してコンポーネントのstate
を更新します。 また、setState
を使用して状態を変更すると、コンポーネントは変更された状態で再レンダリングされます。
そのような単純な!
不必要なリクエストの防止
入力を更新するたびにリクエストが送信されることに気付くかもしれません。 これは、特に大きな応答を受信する場合に、要求の過負荷につながる可能性があります。
この問題の動作を確認するには、ブラウザのDevToolsで[ネットワーク]タブを開きます。 [ネットワーク]タブをクリアします。 入力に映画の名前を入力します。
ご覧のとおり、キーストロークが発生するたびにすべてのデータがダウンロードされます。 この問題を解決するために、src
ディレクトリにutils.js
ファイルを作成しましょう。
$ cd src
$ touch utils.js
次のコードをutils.js
にコピーします。
import axios from 'axios';
const makeRequestCreator = () => {
let token;
return (query) => {
// Check if we made a request
if(token){
// Cancel the previous request before making a new request
token.cancel()
}
// Create a new CancelToken
token = axios.CancelToken.source()
try{
const res = await axios(query, {cancelToken: cancel.token})
const result = data.data
return result;
} catch(error) {
if(axios.isCancel(error)) {
// Handle if request was cancelled
console.log('Request canceled', error.message);
} else {
// Handle usual errors
console.log('Something went wrong: ', error.message)
}
}
}
}
export const search = makeRequestCreator()
App
コンポーネントも変更して、新しいユーティリティ関数を使用します。
// ...
import { search } from './utils'
class App extends Component {
// ...
search = async val => {
this.setState({ loading: true });
// const res = await axios(
const res = await search(
`https://api.themoviedb.org/3/search/movie?query=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b`
);
const movies = res;
this.setState({ movies, loading: false });
};
// ...
今何が起こっているのですか?
Axios
には、リクエストをキャンセルできるキャンセルトークンと呼ばれるものがあります。
makeRequestCreator
で、token
という変数を作成します。 次に、リクエストを使用して、token
変数が存在する場合、そのcancel
メソッドを呼び出して、前のリクエストをキャンセルします。 次に、token
に新しいCancelToken
を割り当てます。 その後、指定されたクエリを使用してリクエストを行い、結果を返します。
問題が発生した場合は、catch
ブロックでエラーを検出し、リクエストがキャンセルされたかどうかを確認して処理できます。
今すぐネットワークタブで何が起こるか見てみましょう:
ご覧のとおり、ダウンロードした応答は1つだけです。 現在、ユーザーは使用したものに対してのみ料金を支払います。
HTTPリクエストとレスポンスのキャッシュ
入力に同じテキストを複数回入力すると、そのたびに新しいリクエストが作成されます。
これを修正しましょう。 utils.js
のユーティリティ関数を少し変更します。
const resources = {};
const makeRequestCreator = () => {
let cancel;
return async query => {
if (cancel) {
// Cancel the previous request before making a new request
cancel.cancel();
}
// Create a new CancelToken
cancel = axios.CancelToken.source();
try {
if (resources[query]) {
// Return result if it exists
return resources[query];
}
const res = await axios(query, { cancelToken: cancel.token });
const result = res.data.results;
// Store response
resources[query] = result;
return result;
} catch (error) {
if (axios.isCancel(error)) {
// Handle if request was cancelled
console.log('Request canceled', error.message);
} else {
// Handle usual errors
console.log('Something went wrong: ', error.message);
}
}
};
};
export const search = makeRequestCreator()
ここでは、ダウンロードした応答を保持するresources
定数を作成しました。 また、新しいリクエストを実行するときは、最初にresources
オブジェクトにこのクエリの結果があるかどうかを確認します。 含まれている場合は、その結果を返します。 適切な結果が得られない場合は、新しいリクエストを作成し、結果をresources
に保存します。 簡単です!
すべてを簡単にまとめましょう。input
に何かを入力するたびに:
- 以前のリクエストがある場合はキャンセルします。
- 入力した結果の以前の結果がすでにある場合は、新しいリクエストを行わずにそれを返すだけです。
- その結果が得られない場合は、新しい結果を作成して保存します。
興味のある方は、このアプリのReduxバージョンを thisrepoで見つけることができます。
結論
おめでとうございます🎉🎉🎉! 不要な応答をダウンロードせず、応答をキャッシュするライブ検索機能を構築しました。 Axiosの助けを借りてReactで効率的なライブ検索機能を構築する方法について1つか2つ学んだことを願っています。
現在、ユーザーはできるだけ少ないトラフィックデータを使用しています。 このチュートリアルを楽しんだら、共有してください! 😄
この投稿の最終結果とソースコードは、 thisCodeSandboxにあります。