なぜReactの新しい特性Hookを使うのかについてのいくつかの実践と浅見

8122 ワード

前言
Hookの定義に関する公式文書はこう述べています.
Hook   React 16.8      。          class        state       React   。

簡単に言えば、関数式コンポーネントを使用するときにstateを使うことができ、ライフサイクル関数などの他の特性もあります.
Hookの使い方を知りたいなら、公式ドキュメントとチェン一峰のReact Hooks入門チュートリアルをよく話して、公式ドキュメントとチェン大神の文章を直接見ることをお勧めします.
このブログでは、なぜReactのHookの新しい特性を使うのか、そしてそれがどのような問題を解決したのかだけを説明します.
なぜHookを使うのですか?
まず他の人がどう言っているか見てみましょう.
チェン大神の文章にはサンプルコードが与えられています.
import React, { Component } from "react";

export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }
  render() {
    const { buttonText } = this.state;
    return ;
  }
}

そして以下を提出します.
            ,     ,       " " 。
    React App         ,     ,       。
    Redux,      。

実際、上のコードの「重さ」の一部は書き方の問題に由来しています.彼は「重さ」を持っていないかもしれません.次のclassの書き方を見てみましょう.
import React, { Component } from "react";

export default class Button extends Component {
  state = {
    buttonText: "Click me, please"
  }
  handleClick = () => {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }
  render() {
    const { buttonText } = this.state;
    return ;
  }
}

次に、Hookを使用した関数コンポーネントを比較します.
import React, { useState } from "react";

export default function Button() {
  const [buttonText, setButtonText] = useState("Click me,   please");

  function handleClick() {
    return setButtonText("Thanks, been clicked!");
  }

  return ;
}

私たちが簡略化したclassの書き方でも、Hookの見た目より確かに「重い」ように見えます.
Hookの文法は確かに簡潔だが、この理由はそれほど十分ではない.
チェン大神は同時にReduxの著者Dan Abramovを挙げてコンポーネントクラスのいくつかの欠点をまとめた.
  • の大規模なコンポーネントは、分割と再構築が難しく、テストも困難です.
  • トラフィックロジックは、コンポーネントの様々な方法に分散し、重複ロジックまたは関連ロジックをもたらす.(ここで私はチェン大神が書いたのは少し問題があると思います.各ライフサイクルの方法がもっと正確だと思います)
  • コンポーネントクラスは、render propsおよび高次コンポーネントなどの複雑なプログラミングモードを導入する.

  • この3点はすべて事実で、そこで関数化のコンポーネントがあって、しかし前の関数化のコンポーネントはstateとライフサイクルがなくて、Hookがあればこの痛みを解決することができます.
    また、Hookはそれだけではありません.Hookをカスタマイズすることで、既存のコンポーネントの論理を抽出して多重化することができます.
    ライフサイクルによる重複論理または関連論理をuseEffectで解決
    上に挙げたいくつかの欠点は、第1点と第3点が分かりやすいかもしれませんが、第2点は理解しにくいので、具体的なコードに深く入り込んでこの言葉を理解する必要があります.
    次のコードを見てみましょう.
    import React, { Component } from "react";
    
    export default class Match extends Component {
      state={
        matchInfo:''
      }
      componentDidMount() {
        this.getMatchInfo(this.props.matchId)
      }
      componentDidUpdate(prevProps) {
        if (prevProps.matchId !== this.props.matchId) {
          this.getMatchInfo(this.props.matchId)
        }
      }
      getMatchInfo = (matchId) => {
        //             
        // ...
        this.setState({
          matchInfo:serverResult // serverResult         
        })
      }
    
      render() {
        const { matchInfo } = this.state
        return 
    {matchInfo}
    ; } }

    このようなコードは、私たちのビジネスでよく登場し、試合コンポーネントに入力されたIDを変更することで、この試合コンポーネントの情報を変更します.
    上記のコードでは、ライフサイクルの影響で、ロード完了とId更新時に重複する論理と関連論理を書く必要があります.
    ビジネスロジックがコンポーネントの各ライフサイクルメソッドに分散し、重複ロジックまたは関連ロジックを引き起こすことを理解する必要があります.
    これを解決するために、ReactはuseEffectというフックを提供した.
    しかし、これを話す前に、Reactがもたらした新しい考え:同期を知る必要があります.
    私たちが上記のコードで行ったことは,実際にはコンポーネント内の状態とコンポーネント外の状態を同期させることである.
    だからHookを使う前に、ライフサイクルの考えを捨てて、同期の考えでこの問題を考える必要があります.
    改造後のコードを見てみましょう.
    import React, { Component } from "react";
    
    export default function Match({matchId}) {
      const [ matchInfo, setMatchInfo ] = React.useState('')
      React.useEffect(() => {
        //             
        // ...
        setMatchInfo(serverResult) // serverResult         
      }, [matchId])
      
      return 
    {matchInfo}
    ; }

    このコードを見て、上のコードと比較して、あなたの心の中の最初の反応は:簡単です.
    React.useEffectは、2つのパラメータを受け入れます.最初のパラメータはEffect関数で、2番目のパラメータは配列です.
    コンポーネントのロード時にEffect関数を実行します.
    コンポーネントの更新は、配列内の各値が変動しているかどうかを判断し、変更しない場合はEffect関数は実行されません.
    2番目のパラメータが送信されない場合、Effect関数はロードされても更新されても実行されます.
    ちなみに、ここにはコンポーネントのロードと更新のライフサイクルの概念があります.では、コンポーネントのアンインストールの概念もあるはずです.
    import React, { Component } from "react";
    
    export default function Match({matchId}) {
      const [ matchInfo, setMatchInfo ] = React.useState('')
      React.useEffect(() => {
        //             
        // ...
        setMatchInfo(serverResult) // serverResult         
    
        return ()=>{
          //           
        }
      }, [matchId])
      
      return 
    {matchInfo}
    ; } }

    これはイベントバインド解縛などによく用いられる.
    カスタムHookでハイレベルコンポーネントを解決
    Reactの高次コンポーネントは繰り返し論理を抽出するためのコンポーネントファクトリであり,簡単に言えば関数であり,入力パラメータはコンポーネントAであり,ある論理を持つコンポーネントA+を出力する.
    上のMatchコンポーネントを思い出すと、このコンポーネントがページAのトップページヘッダで試合情報を表示するために使用されている場合、ページBのサイドバーでも試合情報を表示する必要があります.
    問題は、ページAのUIにはdivが必要であり、ページBのサイドバーのUIにはspanが必要であることである.
    今日早く退勤することを保証する方法は、AページのコードをページBにコピーし、renderのUIを変更すればいいです.
    後で早く退勤することを保証する方法は、上位コンポーネントを使用することです.次のコードを見てください.
    import React from "react";
    
    function hocMatch(Component) {
      return class Match React.Component {
        componentDidMount() {
          this.getMatchInfo(this.props.matchId)
        }
        componentDidUpdate(prevProps) {
          if (prevProps.matchId !== this.props.matchId) {
            this.getMatchInfo(this.props.matchId)
          }
        }
        getMatchInfo = (matchId) => {
          //             
        }
        render () {
          return (
            
          )
        }
      }
    }
    
    const MatchDiv=hocMatch(DivUIComponent)
    const MatchSpan=hocMatch(SpanUIComponent)
    
    
    

    しかし、実際にはreact-reduxのconnectのような高次コンポーネントが複雑になることがあります.これが高次コンポーネントの複雑化の使い方です.
    たとえば、
    hocPage(
      hocMatch(
        hocDiv(DivComponent)
      )
    )

    高次コンポーネントは多くの論理を多重化できることは間違いありませんが、複雑すぎる高次コンポーネントは、その後のメンテナンス者を退却させます.
    Hookの遊び方はカスタムHookを使ってこれらの論理を精錬し、まず私たちが前にHookの関数式コンポーネントを使ったことを見てみましょう.
    import React, { Component } from "react";
    
    export default function Match({matchId}) {
      const [ matchInfo, setMatchInfo ] = React.useState('')
      React.useEffect(() => {
        //             
        // ...
        setMatchInfo(serverResult) // serverResult         
      }, [matchId])
      
      return 
    {matchInfo}
    ; }

    次に、Hookをカスタマイズします.
    function useMatch(matchId){
      const [ matchInfo, setMatchInfo ] = React.useState('')
      React.useEffect(() => {
        //             
        // ...
        setMatchInfo(serverResult) // serverResult         
      }, [matchId])
      return [matchInfo]
    }

    次に、元のマッチコンポーネントを修正します
    export default function Match({matchId}) {
      const [matchInfo]=useMatch(matchId)
      return 
    {matchInfo}
    ; }

    高次コンポーネントよりもカスタムHookの方が簡単で分かりやすいです.
    次のような状況を処理します.
    hocPage(
      hocMatch(
        hocDiv(DivComponent)
      )
    )

    私たちのコードでは、このようなネストは発生しません.次のようになります.
    export default function PageA({matchId}) {
      const [pageInfo]=usePage(pageId)
      const [matchInfo]=useMatch(matchId)
      const [divInfo]=useDiv(divId)
    
      return 
    • {pageInfo}
    • {matchInfo}
    • {divInfo}
    }

    古いclassコンポーネントを改造する必要がありますか?
    今私たちはHookの良いことを知っているので、古いclassコンポーネントを改造する必要があります.
    公式推奨では、hookのためにclassコンポーネントを改造する必要はなく、class関連機能の更新を継続することを保証します.
    実際には、作業量が少なくないため、古いプロジェクトのclassコンポーネントを改造する必要はありません.
    しかし、私たちは新しいプロジェクトや新しいコンポーネントでそれを使用することができます.
    まとめ
    Hookは関数コンポーネントの強化であり、関数コンポーネントがclassコンポーネントのstateとライフサイクルを実現できるようにします.
    Hookの文法はより簡潔で分かりやすく,classのライフサイクル法による重複論理コードを排除し,高次コンポーネントの理解と使用困難の問題を解決した.
    しかし、Hookは関数コンポーネントにclassコンポーネントができないことをさせていません.多くのことをもっと簡単にするだけです.
    classコンポーネントは消えませんが、hook化された関数コンポーネントがトレンドになります.