[反応技術]第8章Hooks


hooksって何ですか?^^
また、ユーザ状態管理機能や、レンダリング後のタスクのユーザ効果->既存の関数構成部品ではできない機能も用意されています.より多くの作業を支援
  • 今日の実習は?ストリーム:
    応答内蔵Hooks->カスタムHooksの作成
  • まず、端末でcreate-act-appを使用して新しいプロジェクトを作成します。


    yarn create react-app hooks-tutorial
    ダメな場合↓
    npm create react-app hooks-tutorial

    8.1ユーザーステータス(3章参照)

  • useStateは最も基本的なHookであり、関数素子を可変状態に保つことができる
  • .
  • の機能要素でステータスを管理する必要がある場合->
  • を使用
    useState機能によるデジタルカウンタの実装
    import React, { useState } from "react"; //useState는 코드 상단에서 import 구문을 통해 불러옴
    
    const Counter = () => {
      const [value, setValue] = useState(0); // 이와 같이 사용 -> 파라미터에는 상태의 기본값인 '0'을 설정하여 넣어준다
      // 그 배열의 첫 번째 원소는 상태 값, 두 번째 원소는 상태를 설정하는 함수
    
      return (
        <div>
          <p>
            현재 카운터 값은 <b>{value}</b> 입니다.
          </p>
          <button onClick={() => setValue(value + 1)}>+1</button>
          <button onClick={() => setValue(value - 1)}>-1</button>
        </div>
      );
    };
    
    export default Counter; // 호출시 배열을 반환한다

  • この関数にパラメータを追加し、呼び出し時に渡されるパラメータに値を置き換えます->構成部品が正常にレンダリングされます

  • App.jsをレンダリングします^^
  • import React from "react";
    import Counter from "./Counter";
    
    const App = () => {
      return <Counter />;
    };
    
    export default App;
    // 터미널에 1. 또는 2. 입력 하여 개발서버 구동
    // 1. yarn start
    // 2. npm start
  • (クラスに変換する必要はありません)

    8.1.1複数使用usState


  • 1つのuseState関数で管理できるのは->1つのステータス値のみです

  • 複数のステータスを管理する必要がある場合は?->複数使用useState

  • srcディレクトリのInfo.jsファイルの作成
  • import React, { useState } from "react";
    
    const Info = () => {
      const [name, setName] = useState("");
      const [nickname, setNickname] = useState("");
      // 다음과 같이 useState를 여러개 사용하여 관리할수 있음
    
      const onChangeName = (e) => {
        setName(e.target.value);
      };
    
      const onChangeNickname = (e) => {
        setNickname(e.target.value);
      };
    
      return (
        <div>
          <div>
            <input value={name} onChange={onChangeName} />
            <input value={nickname} onChange={onChangeNickname} />
          </div>
          <div>
            <div>
              <b>이름:</b> {name}
            </div>
            <div>
              <b>닉네임:</b>
              {nickname}
            </div>
          </div>
        </div>
      );
    };
    
    export default Info;
  • App.jsレンダリング
  • import React from "react";
    import Info from "./Info";
    
    const App = () => {
      return <Info />;
    };
    
    export default App;
  • 出力画面
  • 8.2 useEffect


    userEffectって何ですか?^^
    反応素子をレンダリングするたびに特定の操作を実行するように設定できるHook.
    クラス構成部品の構成部品DidMount+構成部品DidUpdate=UserEffect
  • 既存のInfoコンポーネントUSEffect
  • を適用
    import React, { useState, useEffect } from "react";
    
    const Info = () => {
      const [name, setName] = useState("");
      const [nickname, setNickname] = useState("");
    
      useEffect(() => {
        console.log("렌더링 완료 ~ !");
        console.log({
          name,
          nickname,
        });
      });
    
      const onChangeName = (e) => {
        setName(e.target.value);
      };
    
      const onChangeNickname = (e) => {
        setNickname(e.target.value);
      };
    
      return (
        <div>
          <div>
            <input value={name} onChange={onChangeName} />
            <input value={nickname} onChange={onChangeNickname} />
          </div>
          <div>
            <div>
              <b>이름:</b> {name}
            </div>
            <div>
              <b>닉네임:</b>
              {nickname}
            </div>
          </div>
        </div>
      );
    };
    
    export default Info;

    8.2.1マウント時のみ実行

  • は最初のスクリーンレンダリング時のみ実行され、更新時には実行されませんか?->関数の2番目のパラメータを使用して、空の配列
  • を配置します.
    import React, { useState, useEffect } from "react";
    
    const Info = () => {
      const [name, setName] = useState("");
      const [nickname, setNickname] = useState("");
    
      useEffect(() => {
        console.log("마운트될 때만 실행됩니다.");
      }, []); // useEffect 코드를 다음과 같이 변경
    
      const onChangeName = (e) => {
        setName(e.target.value);
      };
    
      const onChangeNickname = (e) => {
        setNickname(e.target.value);
      };
    
      return (
        <div>
          <div>
            <input value={name} onChange={onChangeName} />
            <input value={nickname} onChange={onChangeNickname} />
          </div>
          <div>
            <div>
              <b>이름:</b> {name}
            </div>
            <div>
              <b>닉네임:</b>
              {nickname}
            </div>
          </div>
        </div>
      );
    };
    
    export default Info;
    // 콘솔창에 한번만 실행된다.

    8.2.2特定値の更新時のみ実行


  • useEffectを使用する場合、一部の値が変更された場合にのみ呼び出されますか?

  • クラスエレメントなら?
  • componentDidUpdate(prevProps, prevState) {
    if (prevProps.value !== this.props.value) {
      doSomething();
    }
    }
    // props 안에 들어 있는 value 값이 바뀔 때만 특정 작업을 수행함
  • がuseEffectでこの操作を適用した場合?
    userEffectに渡される2番目のパラメータの配列に、チェックしたい値を入れます.
  • import React, { useState, useEffect } from "react";
    
    const Info = () => {
      const [name, setName] = useState("");
      const [nickname, setNickname] = useState("");
    
      useEffect(() => {
        console.log(name);
      }, [name]); // useEffect 코드를 다음과 같이 수정
    
      const onChangeName = (e) => {
        setName(e.target.value);
      };
    
      const onChangeNickname = (e) => {
        setNickname(e.target.value);
      };
    
      return (
        <div>
          <div>
            <input value={name} onChange={onChangeName} />
            <input value={nickname} onChange={onChangeNickname} />
          </div>
          <div>
            <div>
              <b>이름:</b> {name}
            </div>
            <div>
              <b>닉네임:</b>
              {nickname}
            </div>
          </div>
        </div>
      );
    };
    
    export default Info;
    // 콘솔창에 한번만 실행된다.

    8.2.3整理


  • デフォルトでは、userEffectはレンダリング直後に実行されます.

  • 実行条件は、2番目のパラメータ配列に何を配置するかによって異なります.

  • 構成部品をアンインストールまたは更新する前に、どのような操作を行いますか?userEffectはクリーンアップ関数(cleanup)を返します.

  • useEffectセクションを変更するには、次の手順に従います.
  • import React, { useState, useEffect } from "react";
    
    const Info = () => {
      const [name, setName] = useState("");
      const [nickname, setNickname] = useState("");
    
      useEffect(() => {
        console.log("effect");
        console.log(name);
        return () => {
          console.log("Cleanup");
          console.log(name);
        };
      }); // useEffect 코드를 다음과 같이 수정
    
      const onChangeName = (e) => {
        setName(e.target.value);
      };
    
      const onChangeNickname = (e) => {
        setNickname(e.target.value);
      };
    
      return (
        <div>
          <div>
            <input value={name} onChange={onChangeName} />
            <input value={nickname} onChange={onChangeNickname} />
          </div>
          <div>
            <div>
              <b>이름:</b> {name}
            </div>
            <div>
              <b>닉네임:</b>
              {nickname}
            </div>
          </div>
        </div>
      );
    };
    
    export default Info;
    // 콘솔창에 한번만 실행된다.
  • AppコンポーネントにInfoコンポーネントの可視性を変更
    import React, { useState } from "react";
    import Info from "./Info";
    
    const App = () => {
      const [visible, setVisible] = useState(false);
      return (
        <div>
          <button
            onClick={() => {
              setVisible(!visible);
            }}
          >
            {visible ? "숨기기" : "보이기"}
          </button>
          <hr />
          {visible && <Info />}
        </div>
      );
    };
    
    export default App;
    
    // 컴포넌트가 나타날시 -> 콘솔에 effect
    // 컴포넌트가 사라질때 -> 콘솔에 cleanup

  • レンダリングするたびに、ポストアレンジ関数が表示されます.

  • 呼び出し後の整理関数の表示時に更新される前の値

  • アンインストール時にクリーンアップ関数だけを呼び出したい場合は?
    userEffect関数の2番目のパラメータに空の配列を追加
  • useEffect(() => {
      console.log("effect");
      console.log(name);
      return () => {
        console.log("Cleanup");
        console.log(name);
      };
    }, []); // 두번째 파라미터에 비어있는 배열 추가

    8.3 useReducer


    userreducerとは?
    Hook(17章減速機の概念を参照)は、素子によって異なる状態を異なる値に更新するために使用される.
    Reducerとは?
    現在のステータスと更新に必要な情報を含むアクション値を渡し、関数->不変性を維持
    function reducer(state, action) {
    return {}; // 불변성을 지키면서 업데이트한 새로운 상태를 반환
    }
  • 動作値は、
  • の形式で構成する.
    {
      type:INCREMENT,
      // 다른 값들이 필요하다면 추가로 들어감
    }
  • UseReducerで使用されるアクションオブジェクトには、タイプがある必要はありません.また、非オブジェクト文字列や数値がある必要はありません.

    8.3.1実施カウンタ

    import React, { useReducer } from "react";
    
    function reducer(state, action) {
      // action.type에 따라 다른 작업 수행
      switch (action.type) {
        case "INCREMENT":
          return { value: state.value + 1 };
        case "DECREMENT":
          return { value: state.value - 1 };
        default:
          // 아무것도 해당되지 않을 때 기존 상태 반환
          return state;
      }
    }
    const Counter = () => {
      const [state, dispatch] = useReducer(reducer, { value: 0 }); // 첫번째 파라미터에는 리듀서 함수, 두번째 파라미터에는 해당 리듀서의 기본값을 넣어줌
      // state는 현재 가리키고 있는상태
      // dispatch는 액션을 발생시키는 함수 -> ex) dispatch(action)함수 내 파라미터로 액션 값을 넣어주면 리듀서 함수가 호출
    
      return (
        <div>
          <p>
            현재 카운터 값은 <b>{state.value}</b> 입니다.
          </p>
          <button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
          <button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
        </div>
      );
    };
    
    export default Counter;
  • の最大の利点は?
  • は、構成部品更新ロジックを構成部品から除外できることを示す
  • App.jsで
  • をレンダリングする
    import React from "react";
    import Counter from "./Counter";
    
    const App = () => {
      return <Counter />;
    };
    
    export default App;

    8.3.2入出力状態の管理


  • usereducer->Infoコンポーネントの入力ステータスの管理
    usereducerを使用する場合->入力ラベルにname値->e.targetを指定します.タスクは、name->Stateの設定と同様に処理されます.

  • Info.jsは以下のように変更されます.
  • import React, { useReducer } from "react";
    // useReducer에서의 액션은 그 어떤 값도 사용 가능
    
    function reducer(state, action) {
      return {
        ...state,
        [action.name]: action.value,
      };
    }
    
    const Info = () => {
      const [state, dispatch] = useReducer(reducer, {
        name: "",
        nickname: "",
      });
      const { name, nickname } = state;
      const onChange = (e) => {
        dispatch(e.target); // 액션값으로 e.target 값 자체를 사용
      };
    
      return (
        <div>
          <div>
            <input name="name" value={name} onChange={onChange} />
            <input name="nickname" value={nickname} onChange={onChange} />
          </div>
          <div>
            <div>
              <b>이름:</b> {name}
            </div>
            <div>
              <b>닉네임: </b>
              {nickname}
            </div>
          </div>
        </div>
      );
    };
    
    export default Info;

    8.4 useMemo


    なぜusemoを使うのですか?
  • 関数型素子内部の演算を最適化します.
  • Average.jsファイル
  • の作成
    import React, { useState } from "react";
    
    const getAverage = (numbers) => {
      console.log("평균값 계산 중..");
      if (numbers.length === 0) return 0;
      const sum = numbers.reduce((a, b) => a + b);
      return sum / numbers.length;
    };
    // getAverage 함수가 호출되는 것을 확인할 수 있음
    
    const Average = () => {
      const [list, setList] = useState([]);
      const [number, setNumber] = useState("");
    
      const onChange = (e) => {
        setNumber(e.target.value);
      };
      const onInsert = (e) => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber("");
      };
    
      return (
        <div>
          <input value={number} onChange={onChange} />
          <button onClick={onInsert}>등록</button>
          <ul>
            {list.map((value, index) => (
              <li key={index}>{value}</li>
            ))}
          </ul>
          <div>
            <b>평균값:</b> {getAverage(list)}
          </div>
        </div>
      );
    };
    
    export default Average;
  • App.jsで
  • をレンダリングする
    import React from "react";
    import Average from "./Average";
    
    const App = () => {
      return <Average />;
    };
    
    export default App;

  • usemo hook->最適化タスクの使用

  • レンダー中に値の一部が変化した場合にのみ実行->演算

  • Average.jsファイルの変更
  • import React, { useState, useMemo } from "react";
    
    const getAverage = (numbers) => {
      console.log("평균값 계산 중..");
      if (numbers.length === 0) return 0;
      const sum = numbers.reduce((a, b) => a + b);
      return sum / numbers.length;
    };
    
    const Average = () => {
      const [list, setList] = useState([]);
      const [number, setNumber] = useState("");
    
      const onChange = (e) => {
        setNumber(e.target.value);
      };
      const onInsert = () => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber("");
      };
    
      const avg = useMemo(() => getAverage(list), [list]);
    
      return (
        <div>
          <input value={number} onChange={onChange} />
          <button onClick={onInsert}>등록</button>
          <ul>
            {list.map((value, index) => (
              <li key={index}>{value}</li>
            ))}
          </ul>
          <div>
            <b>평균값:</b> {avg}
          </div>
        </div>
      );
    };
    
    export default Average;
    // list 배열의 내용이 바뀔때만 getAverage 함수가 호출

    8.5 useCallback


    useCallbackとは?
    userMemoによく似た関数
    レンダリングのパフォーマンスを最適化する必要がある場合に使用
    イベントハンドラ関数が必要な場合にのみ作成できます
  • useCallbackを使用してAverageを行います.jsコンポーネント最適化(onChange、onInsert関数、レンダリングのたびに作成される関数->最適化)
  • import React, { useState, useMemo, useCallback } from "react";
    
    const getAverage = (numbers) => {
      console.log("평균값 계산 중..");
      if (numbers.length === 0) return 0;
      const sum = numbers.reduce((a, b) => a + b);
      return sum / numbers.length;
    };
    
    const Average = () => {
      const [list, setList] = useState([]);
      const [number, setNumber] = useState("");
    
      const onChange = useCallback((e) => {
        //첫번째 파라미터에는 생성하고 싶은 함수, 두번째 파라미터에는 배열을 넣음
        setNumber(e.target.value);
      }, []); // 컴포넌트가 처음 렌더링될 때만 함수생성
      const onInsert = useCallback(() => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber("");
      }, [number, list]); //number 혹은 list가 바뀌었을 때만 함수 생성
    
      const avg = useMemo(() => getAverage(list), [list]);
    
      return (
        <div>
          <input value={number} onChange={onChange} />
          <button onClick={onInsert}>등록</button>
          <ul>
            {list.map((value, index) => (
              <li key={index}>{value}</li>
            ))}
          </ul>
          <div>
            <b>평균값:</b> {avg}
          </div>
        </div>
      );
    };
    
    export default Average;
  • UseCallbackはHook
  • で、関数を返すときにより便利に使用できます.
  • 数字、文字列、オブジェクト->userMemo
  • などの共通値を繰り返し使用するには
  • 関数を再利用するには、->useCallback
  • // 위 아래 같은 코드라고 할 수 있음
    useCallback(() => {
      console.log("hello world!");
    }, []);
    
    useMemo(() => {
      const fn = () => {
        console.log("hello world!");
      };
      return fn;
    }, []);

    8.6 useRef


    userefとは?
  • Hookは
  • を提供し、refが関数素子で容易に使用できるようにする.
  • Average構成部品の登録ボタンを変更し、入力位置にフォーカスを合わせます.
    import React, { useState, useMemo, useCallback, useRef } from "react";
    
    const getAverage = (numbers) => {
      console.log("평균값 계산 중..");
      if (numbers.length === 0) return 0;
      const sum = numbers.reduce((a, b) => a + b);
      return sum / numbers.length;
    };
    
    const Average = () => {
      const [list, setList] = useState([]);
      const [number, setNumber] = useState("");
      const inpuyEl = useRef(null);
    
    
      const onChange = useCallback(e => {
        setNumber(e.target.value);
      }, []); // 컴포넌트가 처음 렌더링될 때만 함수생성
      const onInsert = useCallback(() => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber("");
      }, [number, list]); //number 혹은 list가 바뀌었을 때만 함수 생성
    
      const avg = useMemo(() => getAverage(list), [list]);
    
      return (
        <div>
          <input value={number} onChange={onChange}ref={inputEl} />
          <button onClick={onInsert}>등록</button>
          <ul>
            {list.map((value, index) => (
              <li key={index}>{value}</li>
            ))}
          </ul>
          <div>
            <b>평균값:</b> {avg}
          </div>
        </div>
      );
    };
    
    export default Average;
  • userefを使用してrefを設定すると、userefによって作成されたオブジェクトの現在の値は、実際の領域
  • を指します.

    8.6.1ローカル変数の使用


  • 他の構成部品のローカル変数を使用する場合、ユーザーはRefを使用できます.
    ローカル変数とは?
    レンダリングを考慮せずに変更可能な値を示します.

  • クラス形式で作成された構成部品の場合->ローカル変数を使用する必要がある場合
  • import React, { Component } from 'react';
    
    
    class MyComponent extends Component {
      id = 1
      setId = (n) => {
        this.id = n;
      }
      printId = () => {
        console.log(this.id);
      }
      render() {
        return (
          <div>
            MyComponent
          </div>
        );
      }
    }
    
    
    
    export default MyComponent;
  • これらのコードを関数要素として使用する場合は?
  • import React, { useRef } from 'react';
     
    const RefSample = () => {
      const id = useRef(1);
      const setId = (n) => {
        id.current = n;
      }
      const printId = () => {
        console.log(id.current);
      }
      return (
        <div>
          refsample
        </div>
      );
    };
     
    export default RefSample;
  • refの値が変更されても、要素
  • はレンダリングされません.
  • レンダリングに関係のない値を管理する場合にのみ、
  • が作成されます.

    8.7カスタムホームページの作成


  • 以前にInfoコンポーネントによって作成された複数の入力を管理するためのReducerを使用する論理を->Inputsを使用するHookに分離する

  • useInputs.jsファイルの作成
  • import { useReducer } from 'react';
     
    function reducer(state, action) {
      return {
        ...state,
        [action.name]: action.value
      };
    }
     
    export default function useInputs(initialForm) {
      const [state, dispatch] = useReducer(reducer, initialForm);
      const onChange = e => {
        dispatch(e.target);
      };
      return [state, onChange];
    }
    ↓Info素子使用
    import React from 'react';
    import useInputs from './useInputs';
     
    const Info = () => {
      const [state, onChange] = useInputs({
        name: '',
        nickname: ''
      });
      const { name, nickname } = state;
     
      return (
        <div>
          <div>
            <input name="name" value={name} onChange={onChange} />
            <input name="nickname" value={nickname} onChange={onChange} />
          </div>
          <div>
            <div>
              <b>이름:</b> {name}
            </div>
            <div>
              <b>닉네임: </b>
              {nickname}
            </div>
          </div>
        </div>
      );
    };
     
    export default Info;

    8.8その他のhooks

  • 他の開発者が作成したHooksは、ライブラリとしてインストール(以下に示す)
  • として使用することもできる.
  • https://nikgraf.github.io/react-hooks/
  • https://github.com/rehooks/awesome-react-hooks
  • 8.9定理

  • 応答でHooksモードを使用すると、クラス構成部品を作成することなく機能の大部分
  • を実現できます.
  • マニュアルでは、新しく作成した構成部品に関数構成部品とHooksを使用することを推奨しています.
  • 以降の開発プロジェクトの場合は、関数型素子を使用することを第一の選択肢とし、必要に応じてクラス素子
  • のみを実施する.