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 ] );