I. ホックとは
hoc(高次コンポーネント)は、コードの構成がリアクトであることを意味し、むしろapiではなく、このデザインパターンは、コードとロジックのリアクトコンポーネントで再利用することができ、一般的に言えば、リアクトコンポーネントは、レンダリング機能を再利用しやすいため、つまり、主にhtmlの出力を担当しています。
高次コンポーネントは、reactコンポーネントで処理するために入力コンポーネントを受け入れ、新しいコンポーネントを返すラッパー関数で返されたコンポーネントです。
II.ホックの実装方法
- コンポーネント内の再利用ロジックの特定
- 上記のロジックに適用する関数を作成します。
- コンポーネントを作成するには、この関数を使用します。
- 呼び出し
2.1 コンポーネント内の再利用ロジックの特定
- コンポーネントの作成
- サーバーへのデータ取り込み
- データを使ったコンポーネントのレンダリング
- データの変化を聞く
- データの変更または違約金の修正に関するインシデント
- 変更したデータを使って再度レンダリング
- コンポーネントの破棄により、リスニング中のデータソースが削除されます。
III.関数の規約
1. hoc関数は、ラップされたコンポーネントのペアに余分なプロップを渡しません。
彼は新しいコンポーネントを生成するためにラップした後、ホック関数は、可能な限り透明である必要があり、違いはありません前に渡されます。この目的は、着信コンポーネントのホック関数が変更された後に考慮することではありませんし、このホックのあいまいさを適用すると、コンポーネントの使用のコンポーネントの戻り値の後に何度もある、意図しない動作のいくつかをもたらすためにコンポーネントを考慮する必要があります。
2.hocは関数です!組み合わせ性を最大化する関数の使い方
hocはコンポーネントを返す関数なので、hocができることと同じことができる関数であれば、これを使うことで、例えば、高次のコンポーネントを返すために使われる高次の関数を定義したり、単にパラメータを受け取らない関数を定義したりすることができます。
注意
1、レンダーメソッドでhocを使わない
我々はすべてのreactは、コンポーネントをレンダリングするrenderメソッドを呼び出すことを知っている、もちろん、reactはまた、パフォーマンスの最適化など、いくつかの追加の作業を行いますコンポーネントの再レンダリングでは、reactは、現在のレンダリング返されたコンポーネントとコンポーネントが等しくないかどうかを判断します===等しい場合reactは再帰的にコンポーネントを更新し、その逆は、彼は徹底的にされます。現在のコンポーネントをレンダリングするために古いケースをアンインストールします。diff アルゴリズムは、コンポーネント識別子を使用して、サブツリーを更新するか、破棄してマウントするかを決定するため、返されたコンポーネントが前のコンポーネントと同じ場合は、新しいサブツリーが再帰的に更新され、そうでない場合は、前のサブツリーが完全にアンインストールされ、コンポーネントを再マウントすると、コンポーネントとそのすべてのサブコンポーネントの状態が失われます。
2、静的メソッドをコピーすることを忘れないでください
reactのコンポーネントは一般的にReactを継承したサブクラスです。
クラスにはstrengthメソッドの他にstaticメソッドがあり、コンポーネントをラップするためにhocを使用すると、元のstaticメソッドが上書きされることを忘れないでください:
class Demo extends React.Compoent{
render(){
return (
<div>{this.props.children}</div>
)
}
}
Demo.echo = function(){
console.log('hello world')
}
Demo.echo();
function DemoHoc(Wrap){
return class extends React.Componet{
render(){
return (
<Wrap>{'hello world'}</Wrap>
)
}
}
}
const App = DemoHoc(Demo);
App.echo();
治療
元のコンポーネントの静的メソッドをホックの中に直接コピーするだけです:
function DemoHoc(Wrap){
const myClass = class extens React.Componet{
render(){
return (
<Wrap>{'hello world'}</Wrap>
)
}
}
myClass.echo = Wrap.echo;
return myClass;
}
しかしその場合、HOCはコピーされる静的メソッドの名前を知る必要があり、前述のHOCの柔軟な使い方と組み合わせて、HOCにメソッドパラメータの名前を受け取らせることができます:
function DemoHoc(Wrap,staticMethods=[]){ //デフォルトは空配列
const myClass = class extends ReaCT.Componet{
redner(){
return (
<Wrap>{'hello world'}</Wrap>
)
}
}
for(const metyhodName of staticMethods){
myClass[methodsName] = Wrap[methodsName];
}
return myClass;
}
const App = DemoHoc(Demo,['echo']);
3.トランスミッション
refはコンポーネントの特別なプロパティであるため、通常のプロップのように受け渡すことはできません。
class Wraped extends React.Componet{
constructor(props){
super(props);
this.state = {
message:''
}
}
echo(){
this.setState({
message:'hell world'
})
}
render(){
return <div>{this.state.message}</div>
}
}
HOCを使って包んでください:
function ExampleHoc(Wrap){
return class extends React.Compoent{
render(){
return <Wrap></Wrap>
}
}
}
const Example = ExampleHoc(Wraped);
次に、このコンポーネントを App コンポーネントの中に入れてレンダリングし、ref を使ってこの返されたコンポーネントを参照し、その echo メソッドを呼び出してみます:
const ref = React.createRef();
class App Extends React.Component{
handleEcho = () =>{
ref.current.echo();
}
render(){
return (
<div>
<Example ref={ref}></Example>
<button onClick={this.handleEcho}>echo</button>
</div>
)
}
}
しかし、ボタンをクリックして子コンポーネントでイベントをトリガしようとするとうまくいかず、echoメソッドがないというエラーが報告されます。実際には、refはHOCによって返された匿名クラスにバインドされており、内部コンポーネントにバインドしたい場合はrefを渡すことができます。 デフォルトでは、refはキーのようにpropsに追加されない特別なプロパティであるため、refを渡すことはできないので、reactはrefを渡す必要性を実現するAPIを提供します。
このapiはReact.forwardRefです。このメソッドはコンポーネントを返す関数を受け付け、コンポーネントから渡されたrefを読み込むことができます:
const ReturnedCompoent =React.forwardRef((props,ref) =>{
//propsにはないref属性を取得できる!.
return; // ref属性を必要とするこのコンポーネントを返す
})
この API を使用する HOC:
function Example(Wrap){
class Inner extends React.Compoent{
render(){
const {forwardRef,...rest} = this.props;
return <Wrap ref={forwardedRef} {...rest}></Wrap> // リネームされたrefをpropsで受け取り、refにバインドする。
}
}
return React.forwardRef((props,ref) =>{ //refを受け取り、forwardedRefにリネームしてpropsに渡す。
return <Inner {..props} forwardedRef={ref}></Inner>
})
}
この時点でエコーを呼ぶことに問題はありません:
handleEcho = () =>{
ref.current.echo();
}
V. 高次コンポーネントの実装
1、属性エージェント
つまり、this.propsは高階コンポーネント内でしか値を取ることができません。言い換えると、this.propsは高階コンポーネント内でしか値を取得できません。 処理済みコンポーネント内で this.propsから値を取得したい場合は、高階コンポーネントから処理済みコンポーネントに値を渡す必要があります。これをプロパティのプロキシと呼びます。
プロパティのプロキシを使えば、高次のコンポーネントをより自由に使えるようになり、より多くのことができるようになります。
アプリコンポーネント内でホームコンポーネントのレンダリングを呼び出す際に値を渡します:
import React from 'react'
import Home from './components/home'
export default class App extends React.Component{
render(){
return (
<div>
{/* ここでホームコンポーネントに値を渡す */}
<Home user="haha"/>
</div>
)
}
}
this".propsを通してホームコンポーネントで受信すると、空のオブジェクトが生成され、値は受信されません:
// ... //
render(){
console.log(this.props) // 出力結果はこうなる:{}
return (
<div> </div>
)
}
// ... //
上位コンポーネント内の this.propsを介して受信され、正常に受信されました:
//...
render(){
console.log(this.props) // 出力結果はこうなる:{user:"haha"}
return (
<Fragment>
{this.state.isHeader?<div>Header</div>:""}
<WrapperComponent />
</Fragment>
)
}
// ...
2.逆相続
高次のコンポーネントでは、返されたコンポーネントは処理されたコンポーネントを直接継承するため、処理されたコンポーネントのデータには this.props から直接アクセスできます。
1.処理されたコンポーネントHomeで、ヘッダを表示するかどうかを制御するisHeaderデータを定義します。
import React,{Component} from 'react'
import layout from '../layout'
class Home extends Component{
constructor(){
super();
// ここで独自のデータを定義する
this.state = {
isHeader:true
}
}
render(){
return (
<div> </div>
)
}
}
export default layout(Home);
- 高レベルコンポーネントでは、ホームコンポーネントの状態データは this.stateから直接取得されます。
import React,{ Fragment } from 'react
export default (WrapperComponent)=>{
return class extends WrapperComponent{
render(){
return (
<Fragment>
{/* ここで処理されたコンポーネントのデータにアクセスし、isHeaderがtrueの場合はHeader */}
{this.state.isHeader?<div>Header</div>:""}
<WrapperComponent />
</Fragment>
)
}
}
}
高次のコンポーネントはHomeコンポーネントからすべてを継承するので、当然、thisを介して作業中のコンポーネントのプロパティにアクセスすることができます。
高次コンポーネント用スーパー
高次コンポーネントでは、逆相続によって処理されたコンポーネントを継承した後、継承したコンポーネントに対して、superを通して継承したコンポーネントのメソッドにアクセスするなど、様々な操作を行うことができます。
継承したコンポーネントをレンダリングします。
高次コンポーネントでは、継承されたコンポーネントをレンダリングする2つの方法があります:
1.タグ名によるダイレクトレンダリング
export default (WrapperComponent)=>{
return class extends WrapperComponent{
render(){
return (
<div>
<WrapperComponent />
</div>
)
}
}
}
2.継承したコンポーネントのredner関数をsuper.redner()で呼び出し、レンダリングを行います。
export default (WrapperComponent)=>{
return class extends WrapperComponent{
render(){
return (
<div>
{super.render()}
</div>
)
}
}
}
スーパー経由で継承されたコンポーネントのメソッドを呼び出す
1.継承したコンポーネントにメソッドとデータを定義します:
import React, { Component } from 'react'
import layout from '../layout'
class Home extends Component {
constructor() {
super();
this.state = {
componentName: "Home "
}
}
render() {
return (
<div> </div>
)
}
handleClick() {
alert(1);
}
componentDidMount() {
console.log("componentDidMount")
}
}
// コンポーネントをエクスポートする場合、上位コンポーネントで処理する。
export default layout(Home);
2、スーパーを介した高次成分での操作
import React,{ Fragment } from 'react'
export default (WrapperComponent)=>{
return class extends WrapperComponent{
render(){
console.log(this.props,"layout")
return (
<div>
{super.render()}
{/* 継承されたコンポーネントのメソッドを操作する。alert(1) */}
<button onClick={super.handleClick.bind(this)}> </button>
</div>
)
}
componentDidMount(){
console.log("abc") // 結果を出力する:abc
{/* 継承したコンポーネントのライフサイクルの動作 */}
super.componentDidMount() // 結果を出力する:componentDidMount
}
}
}