JSステップアップ(一)高次関数HOFと高次コンポーネントHOC(Higher Order Func/Comp)

5825 ワード

一、高次関数(コンポーネント)とは何ですか.作用は何ですか.
子クラスが親クラスを使用する方法は継承によって実現できるが、関連コンポーネント通信(redux)、親が子クラスメソッド(逆継承)を使用するのは、クラス(関数)の機能交差/機能多重化などの問題を解決するために、クラス/関数にクラス/関数(継承)を返すようにクラスに独自の未定義の方法を持たせる.
例えば、react-reduxのconnectメソッドでは、高次コンポーネントが使用されます.React Reduxの接続:
const HOC = connnect(mapStateToProps)(Comp);
// connect            =>
function connect(mapStateToProps) {
  // ...
  return function(Comp) {
    // ...
  }
}
//         
const connect = mapStateToProps => Comp => {...};

二、高次関数による二つの無関係関数の通信を実現する
需要説明
データSubClassオブジェクトがあり、stateの2つの関数メソッドがあるクラスsetState getState(サブクラス)が存在する.SupClass1(スーパー/親1)でSubClass(子)のsetStateメソッドを呼び出し、SupClass2(スーパー/親2)でgetStateメソッドで結果を出力することが望ましい.
子はsub、親はsup
ファイルディレクトリ
├  ├── SubClass.js              #   
├  ├── SupClass1.js             #   1
├  ├── SupClass2.js             #   2
├── index.html
SubClassクラスにデータstateを追加し、クエリーと変更の能力を付与
// SubClass.js
class SubClass {
  constructor(args = {}) {
    this.state = {
      ...args,
    };
  }
  //           
  setState = (key, val) => {
    this.state = {
      [key]: val,
    };
  };
  getState = (key) => {
    if (key in this.state) {
      return this.state[key];
    }
    //          
    const err = '  key ';
    throw err;
  };
}
SubClassの機能を試してみましょう
// index.html
const subcls = new SubClass({name: 'xiaobe'});
const res = subCls.getState('name');
console.log('res', res);
//   xiaobe,   

次にSupClass1setStateの能力を与えます
class SuperClass1 {
  set(key, val) {
    // SuperClass1   setState  !
    this.setState(key, val);
  }
}

クラスのgetメソッドを直接実行すると、エラーが発生するに違いありません.SupClass1に何かをする必要がありますSuperClass1クラスにメソッドsetStateを追加する必要があります.継承を使用できます.
// SuperClass1.js
class SuperClass1 extends SubClass {
  constructor(props) {
    super(props);
  }
  set(key, val) {
    //   1      setState  
    this.setState(key, val);
  }
}

// index.html
const supCls1 = new SuperClass1({name: 'sup-xiaobe'});
const res = supCls1.getState('name');
console.log(res);
//     sup-xiaobe

しかし、継承方式を単純に使うと多くのトラブルを引き起こす.たとえば、子クラスと親クラスに同じ名前のメソッドがある場合、デフォルトの子クラスはベースクラス(親クラスの他の呼び方)の同じ名前のメソッドを上書きします.ベースクラスメソッドが関数バインドまたは矢印関数を使用している場合、thisの指向が変更され、ベースクラスを指し、独自の同じ名前のメソッドが無効になります.
そのため、高次コンポーネントによって実現する必要があります.
まず、サブクラスSubClassにHOCエントリを追加します.
class SubClass {
  // ...
  HOC(cls) {
    //     SubClass    ,        this
    const sub_this = this;
    //              ,          !
    return class extends cls {
      constructor(props) {
        super(props);
        //   this     ,sub_this  SubClass 
        this.getState = sub_this.getState;
        this.setState = sub_this.setState;
      }
    }
  }
  // ...
}

次に、親1 SupClass1をインスタンス化する前にアップグレードします(HOCを呼び出します).
// index.html
const subCls = new SubClass();
//             HOC  
const supClsHoc1 = subCls.HOC(SuperClass1);
//      
const supCls1 = new supClsHoc1();
//     state.name
supCls1.set('name', 'sup-xiaobe');

console.log(supCls.getState('name'));
//   sup-xiaobe

同様にSupClass2を完了
// SupClass2.js
class SuperClass2 {
  get(key) {
    return this.getState(key);
  }
}

//    index.html
const subCls = new SubClass({name: 'xiaobe'});
const supClsHoc1 = subCls.HOC(SuperClass1);
const supClsHoc2 = subCls.HOC(SuperClass2);
const supCls1 = new supClsHoc1();
const supCls2 = new supClsHoc2(); 

supCls1.set('name', 'sup-xiaobe');
const res = supCls2.get('name');
console.log('res', res);

このような基礎的な簡単なコンポーネント通信が完了しました.
この考え方によって、グローバル変数のようなStore.jsをカプセル化することができます.
問題を考える
getState setState      this  ?

まずSubClassの完全なコードを列挙します
class SubClass {
  constructor(args = {}) {
    this.state = {
      ...args,
    };
  }
  //        !
  setState = (key, val) => {
    this.state = {
      [key]: val,
    };
  };
  //        !
  getState = (key) => {
    if (key in this.state) {
      return this.state[key];
    }
    return "";
  };
  HOC(cls) {
    const sub_this = this;
    return class extends cls {
      constructor(props) {
        super(props);
        //   this     ,sub_this  SubClass 
        this.getState = sub_this.getState;
        this.setState = sub_this.setState;
      }
    }
  }
}

メソッド内でクラスのデータを使用している場合、私は矢印関数を使用していることがわかります.矢印関数thisを使用しないと、どのような問題が発生するか考えてみましょう.
getState(key) {
  // ...
}

我々は実行する
const subCls = new SubClass({name: 'xiaobe'});
const supClsHoc2 = subCls.HOC(SuperClass2); 
const supCls2 = new supClsHoc2(); 
console.log('supCls2', supCls2.get('name'));

ブラウザが直接エラーを報告していることがわかります:Cannot use'in'operator to search for'name'in undefinedSuperClass2類にはstateは存在しないからです!言い換えればthis.state = undefinedであり、undefinedで対象メソッドを使用すると、当然エラーが発生する.
問題点を特定します(SubClassクラスのデータ/メソッドが必要です).次のいくつかの解決策があります.
// 1. HOC     state
this.state = sub_this.state;

// 2.getState      ,  this    SubClass  
getState = key => { ... };

// 3.    getState  this   SubClass  
this.getState = sub_this.getState.bind(sub_this);

これでthisの誤った問題が解決しました.thisの全面的な解析について、私のこの文章を見てください.
---後述
React HOC