[react]-第9課


Webコンポーネント


Webコンポーネントは、カスタムコンポーネントを作成する標準です.
カスタマイズされたHTML構造、カプセル化されたスタイル、独自のプロパティ、メソッドなどは、データム構成部品によって定義できます.

構成部品の条件

  • コンポーネントクラス(ES 6クラス構文)
  • コンポーネントクラス内部のみで管理するDOM構造(パッケージ)
  • .
  • コンポーネントクラスにのみ適用するCSSスタイル(パッケージ)
  • API
  • は、他のコンポーネントと対話するためのイベント、クラス、および方法を含む

    必要な技術仕様


    技術仕様説明Custom elementsカスタムHTML要素を定義します.Shadow DOM要素の内部に非表示DOMを作成するために使用されます.CSS Scoping要素シャドウDOMの内部にのみ適用されるスタイルを宣言します.Event retargetingコンポーネントを開発環境に適合させるために使用される.

    カスタム要素


    独自のプロパティ(メソッドを含む)やイベントなどを使用して、クラスで説明されているカスタムHTML要素を作成できます.
    カスタム要素が定義されている場合は、<div>などの他のHTML標準要素と同じ内容を使用できます.웹 컴포넌트 예시
    class EuidInput extends HTMLElement {
      constructor() {
        // 요소가 생성될 때 호출된다.
        // HTMLElement를 확장하여 super()가 반드시 필요!
        super();
        
        // 섀도우 돔 트리를 연결하고 이에 대한 참조 반환
        // mode: open은 루트 외부의 자바스크립트에서 접근을 가능하게 해줌
        this.attachShadow({ mode: 'open' });
      }
    
      connectedCallback() {
        // 요소가 문서에 추가될 때 이 메서드 호출
        // 요소가 반복적으로 추가/제거될 경우 수차례 호출 
      }
    
      disconnectedCallback() {
        // 요소가 문서에서 제거될 때 브라우저는 이 메소드를 호출
        // 요소가 반복적으로 추가/제거될 경우 수차례 호출
      }
    
      // static 메서드 
      static get observedAttributes() {
        return [
          /* 변경을 관찰할 속성 이름 배열 */
        ];
      }
    
      // React와 같이 속성이 변경됨에 따라 callback으로 관리
      attributeChangedCallback(name, oldValue, newValue) {
        // observedAttributes()에 등록된 속성 중 값이 변경되면 호출
      }
    
      adoptedCallback() {
        // 요소가 새 문서로 이동되면 호출
        // document.adoptNode에서 발생하며, 거의 사용되지 않는다.
      }
    
      // 사용자가 정의한 속성 및 메서드를 작성할 수 있습니다.
      // ...
    }
    ブラウザで認識できるデータム要素の名前とクラスを定義します.
    customElements.define('euid-input', EuidInput);
    クラスは、defineで定義されたときに小文字케밥 케이스として定義されることが多い必要があります.
    HTMLドキュメントでCustom要素を使用する場合は、次のように記述します.
    <euid-input />
    JavaScriptを使用してカスタム要素を作成することもできます.
    const euidInputNode = document.createElement('euid-input');

    Webコンポーネントの動的インストール

    <script>
      // [미션]
      // 위에 HTML로 작성된 euid-counter 커스텀 요소를 프로그래밍 방식으로 생성한 후,
      // .demo 요소 내부에 마운트 하여 컴포넌트를 렌더링 합니다.
    
      // 컴포넌트가 존재한다면? 컴포넌트를 렌더링 하자.
      let componentName = 'euid-counter';
      customElements.whenDefined(componentName).then(() => {
        const createdCustomElement = document.createElement(componentName);
        createdCustomElement.setAttribute('min', 1);
        createdCustomElement.setAttribute('count', 3);
        createdCustomElement.setAttribute('max', 10);
        document.querySelector('.demo:last-of-type').append(createdCustomElement);
      });
    </script>
    名前付き要素が定義されている場合、whenDefinedメソッドは解析のプロセスを返します.したがって、euid-counterというカスタム要素が定義されている場合、上記のコードなどのコンポーネントが作成され、.demoクラスの最後に動的にマウントされます.
    ただし、この過程でstatic get obsetvedAttributesメソッドとattributeChangedCallbackが存在しない場合、ボタンをクリックして値を変更するとminとmaxの値を超え、内部のカウント値が変更されます.EunidCounter/index.js
    // [미션]
    // 프로그래밍 방식으로 업데이트 된 속성을 감지하여
    // 각 속성 값을 컴포넌트의 props에 업데이트 합니다.
    // 속성 관찰
    static get observedAttributes() {
      return ['min', 'max', 'step', 'count'];
    }
    
    // 속성 관찰 후 이것들을 props에 값을 넣어줌
    attributeChangedCallback(name, oldValue, newValue) {
      this.props[name] = newValue;
    }
    上記のコードを作成して、プロパティを観察するpropsを設定します.これらのプロパティに変更がある場合は、値をattributeChangedCallbackに変更します.

    コンテナ構成部品(ステータスあり)


    コンテナ構成部品とは?


    Reactが提供する構成部品タイプのクラス構成部品をコンテナ構成部品として使用
    これとは反対の用語->表現素子(以前は関数型素子)ですが、react hooksでstateを得ることができます.
    これにより、構成部品の役割をプレゼンテーションとコンテナに分離し、それぞれ独自の責任を負い、構成部品の重複使用性を向上させ、デバッグを簡素化します.

    Reactの主なタスクは、アプリケーションのステータスを取得し、DOMノードに変換することです。


    クラス構成部品は、インスタンスメンバー(Property)であり、転送属性(Props)とステータス(State)を持つ関数構成部品とは異なります.
    注意:https://ko.reactjs.org/docs/react-component.html#instance-properties
    構成部品を再レンダリングすると、関数構成部品は関数ボディを再実行しますが、クラス構成部品はrenderメソッドのみを再実行します.

    prop vs. state


    propは外部(親)から受信して使用するため、値を読み取り専用で更新することはできません.
    stateは変更可能なデータ(ステータス)であり、更新が必要な構成部品はステータスを設定することで使用できます.この場合、stateは素子が所有するローカルデータであり、適用範囲は現在の素子である.

    実際の操作


    cra(create-act-app)による環境の構成
    npx create-react-app .
    reportWebVitalsファイルは後で配備され、-->WebVitalsを使用してレポートされます.
    // static method (sync)
    import reportWebVitals from './reportWebVitals';
    これは同期方式なのでdynamic importでasync方式に変更します.
    import('./reportWebVitals')
      .then(({ default: reportWebVitals }) => reportWebVitals(console.log))
      .catch(({ message }) => console.error(message));

    Web Vitalsに関する次の情報を表示できます.
    これは構築後に使用されるので、process.envproductionで使用!index.js
    // React 앱은 Node.js 환경에서 컴파일 되므로
    // process.env를 사용할 수 있다.
    const { NODE_ENV: devOrProdMode } = process.env;
    
    // 빌드 할 때만, reportWebvitals 모듈 동적 호출
    if (devOrProdMode.includes('production')) {
      // dynamic import (async)
      // reportWebVitals();
      import('./reportWebVitals')
        .then(({ default: reportWebVitals }) => reportWebVitals(console.log))
        .catch(({ message }) => console.error(message));
    }

    ヘッドの作成

    import React from 'react';
    import { ReactComponent as ReactLogo } from '../../assets/logo.svg';
    
    // stateful component
    export class AppHeader extends React.Component {
      constructor(props) {
        super(props);
    
        // 컴포넌트 상태 설정
        this.state = {
          brand: {
            label: 'React',
            className: 'App-logo',
          },
          description: '',
          learnLink: {
            className: 'App-link',
            href: 'https://reactjs.org',
            text: 'Learn React',
            isExternal: true,
          },
        };
      }
    
      render() {
        const {
          brand: { label, className },
          learnLink,
        } = this.state;
        return (
          <header className='App-header'>
            <ReactLogo title={label} className={className} />
            <p>
              Edit <code>src/App.js</code> and save to reload.
            </p>
            <a
              className={learnLink.className}
              href={learnLink.href}
              target={learnLink.isExternal && '_blank'}
              rel={learnLink.isExternal && 'noopener noreferrer'}
            >
              {learnLink.text}
            </a>
          </header>
        );
      }
    }
    ステータスconstructorは、上記のthis.stateの内部で定義することができるが、constructorを必要とせずにクラスフィールドを使用して定義することができる.
    export class AppHeader extends React.Component {
      ...
      // 이렇게 되면 constructor 생략 가능!
      // 컴포넌트 상태 설정
      this.state = {
        brand: {
          label: 'React',
          className: 'App-logo',
        },
        description: '',
        learnLink: {
          className: 'App-link',
          href: 'https://reactjs.org',
          text: 'Learn React',
          isExternal: true,
        },
      };
    クラスフィールドは標準ではありませんが、コンパイル中にBabelで処理され、すぐに使用できます.

    Webを開くと以下の画面が表示され、reactive dev tool拡張がインストールされている場合は、Componentタブに入り、isExternalという状態のチェックボタンを押して、状態に応じて画面を変更することができます.

    上のHighlight updatewhen~を選択すると、画面で再レンダリングするときに、より容易に認識できます.AppHeader.js
    ...
    
    // stateful component
    export class AppHeader extends React.Component {
     
      // 컴포넌트 상태 설정
      this.state = {
        ...
        description: 'Edit <code>src/App.js</code> and save to reload.',
      };
    
      render() {
        const {
          brand: { label, className },
          description,
          learnLink,
        } = this.state;
        return (
          ...
            <p>{description}</p>
          ...
        );
    ...
    下図に示すように、AppHeaderを作成すると、description<code>タグがJSXによって直接文字列に入ります.
    したがって、以下の方法で作成すればよい.
      getDescription() {
        return ['Edit', ' ', <code key='appEntryFile'>src/App.js</code>, ' ', 'and save to reload.'];
      }
    専用のプラグインがあるそうです.

    クラス構成部品ステータスの更新


    クラスではsetState()をステータス変更方法として使用します.

    注意:https://ko.reactjs.org/docs/react-component.html#setstate
    setState()に渡されるupdaterは、関数タイプを設定し、コールバック関数を2番目のパラメータとして渡すこともできます.(クラスインスタンスメソッドも使用可能)

    コールバックsetStateの2番目のパラメータが必要なのは、setStateの起動方式が非同期であるため、コールバック関数はすべての状態が安定して更新された後に実行されるからです.
    constructorからsetStateメソッドを呼び出すのは意味がなく、render部分でステータスを更新することは絶対にできません.反応状態が変化すると、renderを再び呼び出して画面に描画するため、無限ループに陥るからです.

    クラス構成部品のライフサイクル


    一般的なライフサイクル


  • コンストラクタ->構成部品の作成時に呼び出される
  • レンダー->構成部品のレンダー時に呼び出される
  • コンポーネントDidMount->DOMにマウント後、
  • を呼び出す.
  • コンポーネントDidUpdate->は、コンポーネントの更新後に
  • を呼び出す.
  • コンポーネントWillUnMount->は、コンポーネントを除去しようとするときに
  • を呼び出す.
      // 라이프 사이클 메서드
      // 명령형 프로그래밍
      // 컴포넌트가 실제 DOM에 마운트 된 이후 실행
      componentDidMount() {
        console.log('컴포넌트가 실제 DOM에 마운트 된 이후 실행');
        // DOM 노드에 접근 가능 여부
    
        console.log('componentDidmount', document.querySelector('.App-header'));
    
        // 컴포넌트 상태 업데이트 (시간의 흐름에 따라 제어)
        setTimeout(() => {
          console.log('1000ms 지남', this); // 여기서 this는 컴포넌트 인스턴스
        }, 1000);
      }
    
    
    ライフサイクル後にdomにアクセスし、アクセス可能なWebを作成できます.
    コマンドプログラミングを使用してメンテナンスを行うか、アクセス性を遵守する必要がある場合に使用するライフサイクルメソッドコンポーネントDidmountとコンポーネントDidUpdate.
    その後更新する場合は3種類あります.
  • new props
  • setState()
  • forceUpdate()
  • jQueryを反応器に適用してみる

    yarn add jquery

    モジュールとしてロードされているため、グローバルに定義されていません.
    ComponentDidmountでJクエリーコードを記述する
    // jQuery를 사용해 명령형 프로그래밍
    $('.App-header').animate(
      {
        opacity: 0.1,
      },
      {
        duration: 1000,
      }
    );
    適用するかどうかを確認できます.
    親コンポーネントAppをクラスコンポーネントに置き換えて、コンポーネントWillMountの実行手順を表示します.
    import './App.css';
    import { Component } from 'react';
    
    // stateless component(presentational) -> stateful component
    // functional -> class
    export default class App extends Component {
      state = {
        isShowHeader: true,
      };
      render() {
        return (
          <div className='App'>
            {this.state.isShowHeader ? this.props.children : '이런... 자식 노드가 없습니다.'}
          </div>
        );
      }
    }
    コンポーネントDidUpdateステータスの確認

    注意:https://ko.reactjs.org/docs/react-component.html#componentdidupdate
    
      componentDidUpdate(prevProps, prevState) {
        console.log('컴포넌트가 업데이트 되었습니다!!');
        console.log('이전 props 또는 state', { props: prevProps, state: prevState });
        console.log('현재 props 또는 state', { props: this.props, state: this.state });
      }

    非アクティブライフサイクル


  • getDerivedStateFromProps->転送のステータスとプロパティを取得して設定するときに
  • が呼び出されます.
  • shouldComponentUpdate->エレメントを更新しようとしたときに呼び出される(レンダリングされないかレンダリングされない)
  • getSnapshotBeforeUpdate->コンポーネントの更新前にスナップショットを取得するときに呼び出される
  • getDerivedStateFromProps

      static getDerivedStateFromProps(props, state) {
        // 이 안에서 this 인스턴스에 접근 X
        // 반환하는 것은 컴포넌트의 상태에 합성할 파생 상태(객체)
        return {
          count: props.count ?? 100,
        };
      }
    getDerivedStateFromProps-->静的メソッドはアクセスできないため、パラメータを使用してアクセスする必要があります
    派生ステータスを作成する必要があります
    1.propsによるステータスの更新
    2.propsとstateを比較してステータスを更新する

    craに絶対パスを設定する


    注意:https://create-react-app.dev/docs/importing-a-component/#absolute-imports

    Webpackでの絶対パスの設定


    注意:https://webpack.kr/configuration/resolve/#resolvealias

    昇格state


    shouldComponentUpdate実習


    注意:https://ko.reactjs.org/docs/lifting-state-up.html#gatsby-focus-wrapper
      shouldComponentUpdate(nextProps, nextState) {
        // 조건 확인 (비교)
        // props, state(with derivedState)
        // console.log('현재 props 또는 state', this.props, this.state);
        // console.log('다음 props 또는 state', nextProps, nextState);
    
        // 부모로부터 전달 받은 props의 brand.label 값이 바뀌면
        // 컴포넌트 렌더링을 하지 않는다.
        if (nextProps.brand.label !== this.props.brand.label) {
          // render() 미실행
          console.log('부모 컴포넌트의 리 렌더링 요청을 묵살한다.');
          return false;
        }
    
        // 상황 1.
        // 이전 props와 다음 props의 차이가 없다.
        // 굳이 재조정 알고리즘에 의해 다시 렌더링 될 필요가 없다.
    
        // render() 실행
        return true;
      }
    shouldComponentUpdate()-->パフォーマンスの最適化にのみ使用します.レンダリングを防止するために使用すると、エラーが発生します.
    元々持っていたブランドlabelを親ステータスupに昇格させ、親ステータスupに渡し、親ステータスからpropsを受信して条件に従って処理できます.
    参考資料:https://ko.reactjs.org/docs/react-component.html#shouldcomponentupdate

    getShanpShotBeforeUpdate


    アニメーションUIで人気があります
    特に、チャットアプリケーションを実装するときにこの機能を作成しない場合、ユーザインタフェースは少し歪んだ方法で実行されます.
    getSnapshotBeforeUpdate(prevProps, prevState) {
      // DOM 노드에 접근 후, 정보 값을 읽기
      // 읽은 정보 값을 스냅샷 반환
      // snapshot (꼭 object가 아닐 수도 있음)
      return {
        name: 'this is snapshot',
        version: '0.1.2',
      };
    }
    注意:https://ko.reactjs.org/docs/react-component.html#getsnapshotbeforeupdate