ReactでのコンポーネントライフサイクルのuseEffectフックへの置き換え
序章
React Hooksは、Reactでの開発方法に革命をもたらし、最大の懸念事項のいくつかを解決しています。 useEffectフックを使用すると、反復的なコンポーネントのライフサイクルコードを置き換えることができます。
基本的に、フックはReact機能に「フック」できる特別な機能です。 以前に機能コンポーネントを作成し、それに状態を追加する必要があることに気付いた場合、フックは優れたソリューションです。
Hooksを初めて使用し、概要を知りたい場合は、 ReactHooksの概要を確認してください。
この記事は、あなたがuseStateフックに精通していることを前提としています。 そうでない場合は、恐れることはありません! Reactクラスベースのコンポーネントをステートフックを使用して機能的なコンポーネントに変換するのに少し時間を費やすと、正しい方向に進むことができます。
useEffectについて
useEffectは「副作用を使用する」の略です。 効果は、APIを操作するように、アプリケーションが外界と反応するときです。 これにより、何かが変更されたかどうかに基づいて関数を実行できます。 useEffectでは、componentDidMountとcomponentDidUpdateを組み合わせることもできます。
私たちのアプリについて
事前に作成されたクラスベースのコードを取得して、それを機能コンポーネントに変換します。 reactstrap を使用してフォーマットを簡素化し、axiosを使用して外部ダミーAPIを呼び出します。
具体的には、 jsonplaceholder を使用して、最初のコンポーネントマウントにダミーのユーザーデータを取り込みます。
次に、ユーザーのクリックに基づいてコンポーネントを再レンダリングし、ユーザーに関する追加データを取得します。
入門
開始コードを使用してリポジトリを複製するだけです。
$ git clone https://github.com/alligatorio/use-effect-hook $ npm i $ npm start
コード、特にClassBasedComponent.jsファイルに慣れてください。
このファイルには、componentDidMountとcomponentDidUpdateの2つのライフサイクルメソッドがあることに気付くでしょう。
async componentDidMount() {
const response = await axios
.get(`https://jsonplaceholder.typicode.com/users`);
this.setState({ users: response.data });
};
async componentDidUpdate(prevProps) {
if (prevProps.resource !== this.props.resource) {
const response = await axios
.get(`https://jsonplaceholder.typicode.com/users`);
this.setState({ users: response.data });
}
};
これらは両方ともasyncライフサイクルメソッドであり、 jsonplaceholderAPIを呼び出してユーザーのリストを取り込みます。
componentDidMountでは、最初のレンダリングでと言い、ユーザーデータを取得します。 次に、componentDidUpdateで、propsに変更がないかどうかを確認します。 これは、この例のように、ボタンを押すなど、ユーザーが開始したイベントからトリガーできます。 変更が検出されたら、外に出てデータを再度取得します。
ライフサイクルメソッドをuseEffectフックに凝縮し、関数ベースのコンポーネントを作成します。
コンポーネントを作成する
同じClassBasedComponent.jsファイルを使用するのではなく、FunctionBasedComponent.jsという名前の新しいファイルを作成します。 2つを対比して比較できるように、新しいファイルを作成しています。
ターミナルで、次のコマンドを実行して、ルートディレクトリから新しいファイルを作成できます。
$ touch FunctionBasedComponent.js
開始するには、以下のコードをコピーして新しいファイルに貼り付けてください。
import React, { useState, useEffect } from 'react';
import { Container, Button, Row } from 'reactstrap';
import axios from 'axios';
const FunctionBasedComponent = () => {
return (
<Container className="user-list">
<h1>My Contacts:</h1>
</Container>
)
};
export default FunctionBasedComponent;
次に、App.jsファイルに移動し、FunctionBasedComponent.jsファイルをインポートして、ClassBasedComponentをFunctionBasedComponentに置き換えます。
これで、アプリは次のスクリーンショットのようになります。
useStateで状態を初期化することから始めましょう。
const [ users, setUsers ] = useState([]); const [ showDetails, setShowDetails ] = useState(false);
useStateをすばやく要約し、stateをuseStateフックで初期化するには、変数と、配列内の変数に対応する関数の両方を宣言してから、[ X188X]変数を初期化する引数。
users状態変数は空の配列で初期化され、setUsersの機能が与えられます。showDetails状態変数は、falseの値で初期化され、setShowDetailsの機能が割り当てられます。
API呼び出しを追加する
先に進み、API呼び出しをfetchUsers関数として追加しましょう。
const fetchUsers = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);
setUsers(response.data);
};
基本的に、このasync呼び出しは、以前のcomponentDidMountおよびcomponentDidUpdate関数から取得しています。
useEffect内でasync関数を直接使用することはできないことに注意してください。 非同期関数を呼び出したい場合は、useEffectの外部で関数を定義してから、useEffect内で呼び出す必要があります。
useEffect引数
useEffectフックについて少し話しましょう。 componentDidMountと同様に、useEffectはすぐに関数を呼び出します。
useEffect( () => {}, [ 'value' ]);
デフォルトでは、useEffectは配列値が異なるかどうかを確認し、異なる場合は矢印関数が自動的に呼び出されます。
useEffect( () => {}, [ 'different value' ]);
コードエディタに戻って、fetchUsersを呼び出す最新の関数の下にuseEffectフックを追加しましょう。
以下のコードでは、usersオブジェクトを調べて、変更があるかどうかを確認しています。
useEffect( () => { fetchUsers(users) }, [ users ] );
一般的な問題
- 配列をuseEffectフックに渡さないと、コンポーネントは継続的に繰り返しリロードされます。
useEffect( () => { fetchUsers(users) } );
- 空の配列を渡すと、変数は監視されないため、
componentDidMountとまったく同じように、最初のレンダリングでのみ状態が更新されます。
useEffect( () => { fetchUsers(users) }, [] );
- JavaScriptでオブジェクトを作成するたびに、それはメモリ内の異なるオブジェクトになります。 以下のコードはは同じように見えますが、各オブジェクトが異なるメモリアドレスに格納されているため、ページが再レンダリングされます。 同じロジックがアレイにも当てはまります。
useEffect( () => { fetchUsers(users) }, [{ user: 'Alli Alligator' }] );
等しくない!
useEffect( () => { fetchUsers(users) }, [{ user: 'Alli Alligator' }] );
useEffect関数はクリーンアップ関数を返すか何も返さない必要があります。
別の再レンダリングのトリガーを示すために、以下のコードをコピーしてFunctionBasedComponent.jsファイルに貼り付けます。
import React, { useState, useEffect } from 'react';
import { Container, Button, Row } from 'reactstrap';
import axios from 'axios';
const FunctionBasedComponent = () => {
const [ users, setUsers ] = useState([]);
const [ showDetails, setShowDetails ] = useState(false);
const fetchUsers = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);
setUsers(response.data);
};
useEffect( () => { fetchUsers(users) }, [ users ] );
const handleClick = event => { setShowDetails(!showDetails) };
return (
<Container>
{
users.map((user) => (
<ul key={ user.id }>
<li>
<strong>{ user.name }</strong>
<div>
<Button
onClick={ handleClick }
>
{ showDetails ? "Close Additional Info" : "More Info" }
</Button>
{ showDetails &&
<Container className="additional-info">
<Row>
{ `Email: ${ user.email }` }
</Row>
<Row>
{ `Phone: ${ user.phone }` }
</Row>
<Row>
{ `Website: ${ user.website }` }
</Row>
</Container>
}
</div>
</li>
</ul>
))
}
</Container>
)
}
export default FunctionBasedComponent;
これで、ボタン内にonClickイベントがあります。 ボタンをクリックすると、showDetailsの状態が変更され、再レンダリングがトリガーされてAPIが再度呼び出され、必要な追加の詳細が取り込まれます。
Voilà!
async componentDidMount() {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
this.setState({ users: response.data })
};
async componentDidUpdate(prevProps) {
if (prevProps.resource !== this.props.resource) {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
this.setState({ users: response.data })
}
};
になる:
const fetchUsers = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);
setUsers(response.data);
};
useEffect( () => { fetchUsers(users) }, [ users ] );