Hook

15357 ワード

😁 Hookの概要


HookはReactバージョン16.8からReact要素として新たに追加された.
Hookでは、既存のクラスベースのコードを記述することなく、ステータス値と複数のReact機能を使用できます.
hook機能、ライフサイクル、ステータス制御による

😆 Hookの作成動機


1.構成部品間でステータスロジックを再利用することは困難です.
기존의 방법: render props, 고차 컴포넌트 패턴
      weak: 컴포넌트의 재구성 강요, 코드 추적 어려움
            providers, consumers, 고차 컴포넌트 render props, 
            다른 추상화에 대한 레이어로 둘러싸인 "래퍼지옥"(wrapper hell)
Hook의 방법: 컴포넌트로부터 상태 관련 로직을 추상화 가능
  strength: 독립적인 테스트와 재사용이 가능 ,
            계층의 변화 없이 상태 관련 로직을 재사용할 수 있게 함.
            
*고차 컴포넌트(HOC, higher-order component)
 :컴포넌트 로직을 재사용하기 위한 React의 고급 기술
  컴포넌트가 UI를 props로 변환하는 반면, 
  고차 컴포넌트는 컴포넌트를 다른 컴포넌트로 변환합니다.
 
*Render Props
 :React 컴포넌트 간에 코드를 공유하기 위해 함수 props를 이용하는 간단한 테크닉
  render props 패턴으로 구현된 컴포넌트는 자체적으로 렌더링 로직을 구현하는 대신,
  react 엘리먼트 요소를 반환하고 이를 호출하는 함수를 사용합니다.
 
  
  	<DataProvider render={data => (
  	   <h1>Hello {data.target}</h1>
	)}/>
            
            
2.複雑な要素は理解しにくい.
관리하기가 힘들어지는 상태 관련 로직들과 사이드 이펙트가 
있는 컴포넌트들을 유지보수해야 합니다. 
각 생명주기 메서드에는 자주 관련 없는 로직이 섞여들어가고는 합니다.

ex)
   componentDidMount 와 componentDidUpdate는 컴포넌트안에서 
   데이터를 가져오는 작업을 수행할 때 사용 되어야 하지만,
   같은 componentDidMount에서 이벤트 리스너를 설정하는 것과 같은
   관계없는 로직이 포함되기도 하며,
   componentWillUnmount에서 cleanup 로직을 수행하기도 합니다.
   
weak: 함께 변경되는 상호 관련 코드는 분리되지만 이와 연관 없는 코드들은 
      단일 메서드로 결합합니다. 
      이로 인해 버그가 쉽게 발생하고 무결성을 너무나 쉽게 해칩니다.

でもHookって何?


Hookは、関数コンポーネントでReact Stateとライフサイクル機能(ライフサイクル特性)を「バインド」できる関数です.Hookはクラスでは機能しません.
逆にclassなしでreactを使用できます.

Class Componenet life cycle



📌 State Hook

  import React, { useState } from 'react';

  function Example() {
    // "count"라는 새 상태 변수를 선언합니다
    const [count, setCount] = useState(0);
	//useState -> Hook-> state 추가
        //useState 객체 아니어도 된다
    return (
      <div>
        <p>You clicked {count} times</p>
        //setState 대신 setCount사용
        <button onClick={() => setCount(count + 1)}>
          Click me
        </button>
      </div>
    );
  }

Hookなどの機能のクラス例

  class Example extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 0
      };
    }

    render() {
      return (
        <div>
          <p>You clicked {this.state.count} times</p>
          <button onClick={() => 
          	this.setState({ count: this.state.count + 1 })}>Click me
          </button>
        </div>
      );
    }
  }

クラス構成部品と関数構成部品の違い


  • ステータス変数の宣言
    (この関数は->userState Hookを直接呼び出す場合には適用されません)


  • インポートステータス


  • ステータスの更新
    (関数はsetCountとcount変数を持つのでXを呼び出す)

  • const [fruit, setFruit] = useState('banana'); 意味

      var fruitStateVariable = useState('banana'); // 두 개의 아이템이 있는 쌍을 반환
      var fruit = fruitStateVariable[0]; // 첫 번째 아이템
      var setFruit = fruitStateVariable[1]; // 두 번째 아이템
      
      useState를 이용하여 변수를 선언하면 2개의 아이템 쌍이 들어있는 배열로 만들어집니다.
      첫 번째 아이템은 현재 변수를 의미하고, 
      두 번째 아이템은 해당 변수를 갱신해주는 함수입니다

    const vs let

    useState 사용시 값이 다른 값으로 재할당 될텐데 const를 쓰는 이유:
    
    component가 다시 rendering 되면 함수가 다시 실행되어 새 scope를 만들고 
    새롭게 name 변수를 만들고, 이전 변수와는 관련이 없어지게 되어 const로 선언한다.

    複数のステータス変数を使用する場合は?

    function ExampleWithManyStates() {
      // 여러 개의 state를 선언할 수 있습니다!
      const [age, setAge] = useState(42);
      const [fruit, setFruit] = useState('banana');
      const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
      
      function handleOrangeClick() {
       // this.setState({ fruit: 'orange' })와 같은 효과를 냅니다.
        setFruit('orange');
      }

    🚀 コンポーネントのライフサイクル


    💡 コンポーネントのライフサイクルは、コンポーネントがノードとしてDOMをアップ、変更、およびダウンするプロセスから構成されます.
    👉 つまり、「マウント」(DOMにノードを追加)、「アンインストール」(DOMから削除)、「更新」(DOMにすでに存在するノードを変更)から構成されます.

    📌 マウント


  • 構成部品インスタンスを作成し、DOMに挿入します.
    反応器とは、素子を特定の領域に挿入する動作を指す.

  • 例えば、RealtDOM.render関数により、応答素子をDOMの特定の領域に埋め込み、これらのプロセスをマウントすることを表すことができる.

  • <レンダリングとマウント>
    👉 "Rendering"is any time a function component gets called or a class-based render method gets called which returns a set of instructions for creating DOM.
    👉 "Mounting"is when React "renders"the component for the first time and actually builds the initial DOM from those instructions.
    👉 レンダー(Render)とは、構成部品のDOMを作成するコマンドを返す関数を呼び出し、インストールは構成部品を初めてレンダーすることを意味します.

  • mountフェーズのclass componentライフサイクルメソッド1.constructor()2. static getDerivedStateFromProps()3. render()4. componentDidMount()
  • 📌 更新


  • マウントされたDOMに存在する構成部品を再描画することで更新します.(再マウントではありません.)

  • A "re-render"is when React calls the function component again to get a new set of instructions on an already mounted component. Re-renders just update the DOM but don't mount since mounting just happens once.

  • 構成部品は、次の4つの場合に更新されます.
    -props変更時
    -状態変化時
    -親構成部品を再レンダリングする場合
    - this.forceUpdateを使用してレンダリングを強制トリガします.

  • 更新フェーズのクラスコンポーネントライフサイクルメソッド
    1. static getDerivedStateFromProps()
    2. shouldComponentUpdate()
    3. render()
    4. getSanphotBeforUpdate()
    5. componentDidUpdate()
  • 📌 Unmountアンインストール


  • インストールの逆の手順.DOMから構成部品を削除

  • アンインストール手順で、クラスコンポーネントはcomponentWillUnmount()メソッドを呼び出します.
  • ⚡️ Effect Hook


    副作用とは、


    Reactコンポーネントからデータをインポートまたは購読し、DOMを手動で操作します.
    フィーチャー:他の構成部品に影響を与える可能性がありますが、レンダリング中に実行できないアクション
    Effect Hook(すなわちUSEffect)は、関数要素内でこれらの副作用を実行することを可能にする.
    maineffectはrenderのreturn()部分と考えられる.

    useEffectはライフサイクルを担当します



    useEffect =  componentDidMount       +componentDidUpdate       +componentWillUnmount

    1.マウント時のみ実行(ComponentDidMount)


    userEffectで設定した関数は、エレメントが画面上で最初にレンダリングされたときにのみ実行され、更新時に実行する必要がない場合は、空の配列を関数の2番目のパラメータに入れることができます.
     useEffect(() => {
        console.log('마운트 될 때만 실행됩니다.');
      }, []);

    2.特定の値の更新時のみ実行したい(ComponentDidupdate)


    クラス構成部品の場合、prevPropsの値を取得して比較することで論理を実行します.
    componentDidUpdate(prevProps, prevState) {
      if (prevProps.value !== this.props.value) {
        doSomething();  
      }
    }
    userEffectでは、2番目のパラメータに渡される配列にチェック(更新するかどうか)したい値を入れるだけです.
    useEffect(() => {
        console.log(name);
      }, [name]); //name의 업데이트가 있을경우 render

    3.クリーンアップ(ComponentWillUnmount)


    アンインストール前または更新前にアクションを実行する場合は、userEffectでクリーンアップ関数を返す必要があります.
    useEffect(() => {
        console.log('effect');
        console.log(name);
        return () => {
          console.log('cleanup');
          console.log(name);
        };
      });
    アンインストール時にクリーンアップ関数のみを呼び出す場合は、userEffect関数の2番目のパラメータに空の配列を入れることができます.
     useEffect(() => {
        console.log('effect');
        console.log(name);
        return () => {
          console.log('cleanup');
          console.log(name);
        };
      }, []);

    複数の効果を使用可能

    function FriendStatusWithCounter(props) {
      const [count, setCount] = useState(0);
      useEffect(() => {
        document.title = `You clicked ${count} times`;
      });
    
      const [isOnline, setIsOnline] = useState(null);
      useEffect(() => {
        function handleStatusChange(status) {
          setIsOnline(status.isOnline);
        }
      });
      // ...
    }

    🔒 Hookのルール


    1.トップレベルからのみHookを呼び出す


    繰り返し文、条件文、またはネストされた関数でHookを呼び出す
    Why?
    ルールは、構成部品をレンダリングするたびにHookが同じ順序で呼び出されることを保証します.
    すべてのレンダリングでは、Hookの呼び出し順序が同じなので、例は正常に動作します.
    function Form() {
      // 1. name이라는 state 변수를 사용하세요.
      const [name, setName] = useState('Mary');
    
      // 2. Effect를 사용해 폼 데이터를 저장하세요.
      useEffect(function persistForm() {
        localStorage.setItem('formData', name);
      });
    
      // 3. surname이라는 state 변수를 사용하세요.
      const [surname, setSurname] = useState('Poppins');
    
      // 4. Effect를 사용해서 제목을 업데이트합니다.
      useEffect(function updateTitle() {
        document.title = name + ' ' + surname;
      });
    
      // ...
    }
    
    // ------------
    // 첫 번째 렌더링
    // ------------
    useState('Mary')           // 1. 'Mary'라는 name state 변수를 선언합니다.
    useEffect(persistForm)     // 2. 폼 데이터를 저장하기 위한 effect를 추가합니다.
    useState('Poppins')        // 3. 'Poppins'라는 surname state 변수를 선언합니다.
    useEffect(updateTitle)     // 4. 제목을 업데이트하기 위한 effect를 추가합니다.
    
    // -------------
    // 두 번째 렌더링
    // -------------
    useState('Mary')           // 1. name state 변수를 읽습니다.(인자는 무시됩니다)
    useEffect(persistForm)     // 2. 폼 데이터를 저장하기 위한 effect가 대체됩니다.
    useState('Poppins')        // 3. surname state 변수를 읽습니다.(인자는 무시됩니다)
    useEffect(updateTitle)     // 4. 제목을 업데이트하기 위한 effect가 대체됩니다.
    
    // ...
    条件文はHookの言葉を書きますか?(第2項)
    function Form() {
      // 1. name이라는 state 변수를 사용하세요.
      const [name, setName] = useState('Mary');
    
      // 2. Effect를 사용해 폼 데이터를 저장하세요.
      if (name !== '') {
        useEffect(function persistForm() {
          localStorage.setItem('formData', name);
        });
      }
    
      // 3. surname이라는 state 변수를 사용하세요.
      const [surname, setSurname] = useState('Poppins');
    
      // 4. Effect를 사용해서 제목을 업데이트합니다.
      useEffect(function updateTitle() {
        document.title = name + ' ' + surname;
      });
    
      // ...
    }
    
    
    useState('Mary')           // 1. name state 변수를 읽습니다. (인자는 무시됩니다)
    // useEffect(persistForm)  // 🔴 Hook을 건너뛰었습니다!
    useState('Poppins')        // 🔴 2 (3이었던). surname state 변수를 읽는 데 실패했습니다.
    useEffect(updateTitle)     // 🔴 3 (4였던). 제목을 업데이트하기 위한 effect가 대체되는 데 실패했습니다.
    
    name !== '' 条件
    最初のレンダリング=>true
    次に、レンダリングでフォーム=>falseを初期化します.
    エラーが発生し、Hookの順番が次から次へと
    解決策
      useEffect(function persistForm() {
        // 👍 더 이상 첫 번째 규칙을 어기지 않습니다
        if (name !== '') {
          localStorage.setItem('formData', name);
        }
      });

    2.react関数でのみHookを呼び出す


    通常のJavaScript関数からHookを呼び出す
    Why?
    この規則に従うと、コンポーネントのステータスに関連するすべての論理がソースコードに明確に表示されます.

    💬 自分のHookを作成


    独自のHookを作成した後、構成部品ロジックを関数として抽出することで再利用できます.
    次の図では、2つの関数要素が同じ論理を使用していることを示します.

    ユーザー定義のHookの抽出


    2つのJavaScript関数で同じ論理を共有する場合は、別の関数に分離します.素子とHookも関数なので、同じ方法が使えます!
    カスタムHookはuseで始まるJavaScript関数です.
    
    import { useState, useEffect } from 'react';
    
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
      // friendID를 인수로 받고 온라인 상태의 여부를 반환
    
      useEffect(() => {
        function handleStatusChange(status) {
          setIsOnline(status.isOnline);
        }
    
        ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
        return () => {
          ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
        };
      });
    
      return isOnline;
    }

    共通部分はuserFriendStatus()を用いて簡単な処理を行った.
    このときの注意点は
    1.カスタムHookの名前は「use」で始まる必要があります
    Why?
    関数がHookを呼び出すかどうか分からないため、無効になっている場合、Hookルールに違反しているかどうかを自動的にチェックすることはできません.
    2.同じHookの2つの構成部品を使用してstateを共有する
    Why?
    ステータス関連ロジックを再利用する仕組みですが、ユーザーHookを使用するたびにstateとeffectは完全に独立しています.

    📋 Hook FAQセット


    Q 1:分類できない場合、Hookは何ができるのでしょうか.
    A 1:Hookは強力で表現力のある新しい機能を提供し、素子間で機能を再利用できる(独自のHookを作成する)
    Q2.ライフサイクルメソッドはどのようにHookに応答しますか?
    A2:
  • コンストラクション関数:ユーザーステータスコールでステータスを初期化できます.
    初期状態の計算が高価な場合は、関数要素をユーザー状態に渡すことができます.
  • const [state, setState] = useState(() => {
      const initialState = someExpensiveComputation(props);
      return initialState;
    });
  • getDerivedStateFromProps:レンダリング中に更新を保持します.
  • shouldComponentUpdate: React.memoを参照してください.
  • render:これは関数素子自体です.
  • componentDidMount, componentDidUpdate, componentWillUnmount:
    userEffect Hookは、それらのすべての組合せを表すことができます.
  • GetSnapshotBeforeUpdate、ComponentDidCatch、GetDerivedStateFromError:これらのメソッドにはHookはありませんが、追加されます.
  • Q3.1つ以上のステータス変数を使用する必要がありますか?
    A 3:userState()を1回だけ呼び出し、すべてのステータスを1つのオブジェクトに入れたい場合があります.
    function Box() {
      const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
      // ...
    }
    --------------------------------------------------------------
     // ...
      useEffect(() => {
        function handleWindowMouseMove(e) {
          // "... state"를 spread 하여 너비와 높이가 "손실"되지 않습니다
          setState(state => ({ ...state, left: e.pageX, top: e.pageY }));
        }
        // 주의: 이 구현은 약간 단순화되었습니다
        window.addEventListener('mousemove', handleWindowMouseMove);
        return () => window.removeEventListener('mousemove', handleWindowMouseMove);
      }, []);
      // ...