ReactでのコンポーネントライフサイクルのuseEffectフックへの置き換え

提供:Dev Guides
移動先:案内検索

序章

React Hooksは、Reactでの開発方法に革命をもたらし、最大の懸念事項のいくつかを解決しています。 useEffectフックを使用すると、反復的なコンポーネントのライフサイクルコードを置き換えることができます。

基本的に、フックはReact機能に「フック」できる特別な機能です。 以前に機能コンポーネントを作成し、それに状態を追加する必要があることに気付いた場合、フックは優れたソリューションです。

Hooksを初めて使用し、概要を知りたい場合は、 ReactHooksの概要を確認してください。

この記事は、あなたがuseStateフックに精通していることを前提としています。 そうでない場合は、恐れることはありません! Reactクラスベースのコンポーネントをステートフックを使用して機能的なコンポーネントに変換するのに少し時間を費やすと、正しい方向に進むことができます。

useEffectについて

useEffectは「副作用を使用する」の略です。 効果は、APIを操作するように、アプリケーションが外界と反応するときです。 これにより、何かが変更されたかどうかに基づいて関数を実行できます。 useEffectでは、componentDidMountcomponentDidUpdateを組み合わせることもできます。

私たちのアプリについて

事前に作成されたクラスベースのコードを取得して、それを機能コンポーネントに変換します。 reactstrap を使用してフォーマットを簡素化し、axiosを使用して外部ダミーAPIを呼び出します。

具体的には、 jsonplaceholder を使用して、最初のコンポーネントマウントにダミーのユーザーデータを取り込みます。

次に、ユーザーのクリックに基づいてコンポーネントを再レンダリングし、ユーザーに関する追加データを取得します。

入門

開始コードを使用してリポジトリを複製するだけです。

$ git clone https://github.com/alligatorio/use-effect-hook
$ npm i
$ npm start

コード、特にClassBasedComponent.jsファイルに慣れてください。

このファイルには、componentDidMountcomponentDidUpdateの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ファイルをインポートして、ClassBasedComponentFunctionBasedComponentに置き換えます。

これで、アプリは次のスクリーンショットのようになります。



useStateで状態を初期化することから始めましょう。

const [ users, setUsers ] = useState([]);
const [ showDetails, setShowDetails ] = useState(false);

useStateをすばやく要約し、stateuseStateフックで初期化するには、変数と、配列内の変数に対応する関数の両方を宣言してから、[ 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 ] );