フックで90 %のクリーナーコードを書く方法🎣


2018年は反応生態系に多くの新機能をもたらした.これらの機能の追加は、開発者がより多くのユーザーの経験ではなく、時間を費やすコードロジックを書くに焦点を当てています.
反応は、より堅牢でスケーラブルなUIを構築するための優れたツールを探索する際に、機能的プログラミングパラダイムに向けて投資しているようです.
2018年10月のReactConfでは、反応が嵐によってコミュニティを取ったフックと呼ばれる提案APIを発表しました.開発者は彼らを調査して、実験を始めました、そして、それはRFC(コメントの要請)で大きなフィードバックを受けました.反応16.8.0フックをサポートする最初のリリースです🎉.
この記事は私の試みです.
  • フックが
  • を導入した理由
  • 私たちは、このAPI
  • のためにどのように我々自身を準備することができますか
  • 反応フックを使用して90 %クリーナーコードをどのように書くことができるか🎣
  • あなたがちょうど最初にこの新しいAPIの感じを持ちたいならば、私は遊びにdemoをつくりました.さもなければ、現在直面している3つの主要な問題を見てみましょう.

    1 .コードロジックの再利用
    あなたはすべてのコードロジックを再利用するのは難しい知っているとそれはあなたの頭を得るために経験の公正なビットが必要です.私は約2年前に反応を開始したとき、私はすべての私のロジックをカプセル化するクラスのコンポーネントを作成するために使用.そして、別のコンポーネント間でロジックを共有することになると、私は単に異なるUIをレンダリングする同様に見えるコンポーネントを作成するでしょう.しかし、それは良くなかった.私はDRY原則に違反していて、理想的には論理を再利用していませんでした.

    旧法
    ゆっくりと、私は私のコードロジックを再利用するための関数プログラミングを使用することができたHOCパターンについて学んだ.は、別のコンポーネント(dumb)を取って、新しい強化されたコンポーネントを返す単純なhigher order functionだけです.この強化されたコンポーネントは、ロジックをカプセル化します.

    A higher order function is a function that takes a function as an argument, or returns a function.


    export default function HOC(WrappedComponent){
      return class EnhancedComponent extends Component {
       /*
         Encapsulate your logic here...
       */
    
        // render the UI using Wrapped Component
        render(){
          return <WrappedComponent {...this.props} {...this.state} />
        }
      }
    
      // You have to statically create your
      // new Enchanced component before using it
      const EnhancedComponent = HOC(someDumbComponent);
    
      // And then use it as Normal component
      <EnhancedComponent />
    
    その後、我々はrender propsパターンの上昇を示す小道具として機能を通過する傾向に移動しました.レンダリングプロップは、“レンダリングコントローラ”あなたの手にある強力なパターンです.これはinversion of control(IoC)の設計原理を容易にします.反応ドキュメントは、値が関数である小文字を使用してコンポーネント間のコードを共有するためのテクニックとして説明します.

    A component with a render prop takes a function that returns a
    React element and calls it instead of implementing its own render logic.


    簡単な言葉では、クラスコンポーネントを作成してロジック(副作用)をカプセル化し、レンダリングになると、このコンポーネントは単にUIをレンダリングするために必要なデータだけを渡すことで、関数を呼び出します.
    export default class RenderProps extends Component {
    /*
      Encapsulate your logic here...
    */
    
      render(){
        // call the functional props by passing the data required to render UI
        return this.props.render(this.state);
      }
     }
    
    // Use it to draw whatever UI you want. Control is in your hand (IoC)
    <RenderProps render={data => <SomeUI {...data} /> } />
    
    これらのパターンの両方が再利用されたコード論理問題を解決していたけれども、彼らは以下に示されるようにラッパー地獄問題を残しました:

    それで、コードロジックを再利用することに関連する問題がいくつかあります.
  • を実装するのに非常に直感的ではありません
  • のコード
  • ラッパーヘル

  • 巨大な構成要素
    コンポーネントは、反応のコード再利用の基本単位です.クラスのコンポーネントに複数の振舞いを抽象化しなければならないとき、それはサイズが大きくなり、維持するのが困難になりがちです.

    A class should have one and only one reason to change,
    meaning that a class should have only one job.


    下記のコード例を見ると、次のように推論できます.
    export default class GiantComponent extends Component {
      componentDidMount(){
        //side effects
        this.makeRequest();
        document.addEventListener('...');
        this.timerId = startTimer();
        // more ...
      }
    
      componentdidUpdate(prevProps){
       // extra logic here
      }
    
      componentWillUnmount(){
        // clear all the side effects
        clearInterval(this.timerId);
        document.removeEventListener('...');
        this.cancelRequest();
      }
      render(){ return <UI />; }
    
  • コードは、異なるライフサイクルフックにわたって広げられます
  • ノーシングル責任
  • をテストするのに難しい


  • 3 .人間と機械には難しい
    問題の人間の側を見て、私たちはかつて、子供のコンポーネントの中で関数を呼ぶことを試みました.
    TypeError: Cannot read property 'setState' of undefined
    
    そして、原因を理解しようとして我々の頭をひっかいました:あなたはコンストラクタにそれをバインドするのを忘れていました.したがって、これはいくつかの経験豊富な開発者の間で混乱のトピックのままです.

    this gets the value of object who invokes the function


    また、最初の副作用の実装を開始するには、boilerplateコードの多くを記述する必要があります:
    extends -> state -> componentDidMount -> componentWillUnmount -> render -> return
    
    クラスはマシンにとっても以下の理由でハードです.
  • ミニバージョンは、メソッド名
  • を制限しません
  • 未使用のメソッドは、取り除かれません
  • ホットリロードとコンパイラ最適化では難しい
    上記の3つの問題は3つの問題ではありませんが、これらは1つの単一の問題の症状であり、反応はクラスの構成要素よりも単純な原始的なものではありません.
    新しい反応フック提案APIの出現で、我々は完全に我々の構成要素の外で我々の論理を抽象化することによって、この問題を解決することができます.より少ない語で、あなたは機能的な要素にstatentな論理をフックすることができます.

    React Hooks allow you to use state and other React features without writing a class.


    以下のコード例で見てみましょう.
    import React, { useState } from 'react';
    
    export default function MouseTracker() {
    
      // useState accepts initial state and you can use multiple useState call
    
      const [mouseX, setMouseX] = useState(25);
      const [mouseY, setMouseY] = useState(25);
    
      return (
        <div>
          mouseX: {mouseX}, mouseY: {mouseY}
        </div>
      );
    }
    
    useStateフックへの呼び出しは値のペアを返します:現在の状態とそれを更新する関数.この場合、現在の状態値はmousex、setter関数はsetMousExです.USENTに引数を渡すと、コンポーネントの初期状態となります.
    さて、ここで問題となるのはsetMousExです.USENTフックの下で呼び出すとエラーが発生します.これを呼び出すのと同じです.クラスコンポーネントのレンダリング関数内でsetstate.
    したがって、答えはまた、すべての副作用を実行するためのuseEffectと呼ばれるプレースホルダーフックを提供する反応です.
    import React, { useState } from 'react';
    
    export default function MouseTracker() {
    
      // useState accepts initial state and you can use multiple useState call
      const [mouseX, setMouseX] = useState(25);
      const [mouseY, setMouseY] = useState(25);
    
      function handler(event) {
        const { clientX, clientY } = event;
        setMouseX(clientX);
        setMouseY(clientY);
      }
      useEffect(() => {
        // side effect
        window.addEventListener('mousemove', handler);
    
        // Every effect may return a function that cleans up after it
        return () => window.removeEventListener('mousemove', handler);
      }, []);
    
      return (
        <div>
          mouseX: {mouseX}, mouseY: {mouseY}
        </div>
      );
    }
    
    この効果は、すべての更新後、最初のレンダリングの後に呼び出されます.また、クリーンアップのメカニズムになるオプションの関数を返すことができます.これにより、サブスクリプションの追加と削除のためのロジックを保つことができます.
    有効なコールの2番目の引数は任意の配列です.配列内の要素値が変更されると、あなたの効果は再実行されます.shouldComponentUpdateがどのように機能するかを考えてください.効果を実行し、一度だけマウントしてマウントする場合には、空の配列([])を2番目の引数として渡すことができます.これは、あなたの効果は小道具や状態から任意の値に依存しないことを反応しますので、再実行する必要はありません.これは、コンポーネントのマウントとコンポーネントの使い慣れたメンタルモデルに近いです.あなたが役に立つ効果フックに深いダイビングを望むならば、私はもう一つの記事を書きました.
    しかし、我々のMouseTrackerコンポーネントはまだ内部のロジックを保持していませんか?もう一つのコンポーネントがmousemoveふるまいを共有したいならば、どうですか?また、もう1つの効果(例えば、ウィンドウのサイズ変更)を追加すると、ほとんど管理が難しくなり、クラスのコンポーネントで見たのと同じ問題に戻ります.
    さて、本当の魔法はあなたの機能部品の外であなたのカスタムフックをつくることができます.これは、ロジックを別のモジュールに抽象化し、異なるコンポーネント間で共有することに似ています.それを見てみましょう.
    // you can write your custom hooks in this file
    import { useState, useEffect } from 'react';
    
    export function useMouseLocation() {
      const [mouseX, setMouseX] = useState(25);
      const [mouseY, setMouseY] = useState(25);
    
      function handler(event) {
        const { clientX, clientY } = event;
        setMouseX(clientX);
        setMouseY(clientY);
      }
      useEffect(() => {
        window.addEventListener('mousemove', handler);
    
        return () => window.removeEventListener('mousemove', handler);
      }, []);
    
      return [mouseX, mouseY];
    }
    
    そして今、私たちは、以下に示すように、新しいバージョンにMouseTrackerコンポーネントコード(90 %)をクリーンアップできます.
    import React from 'react';
    import { useMouseLocation } from 'customHooks.js';
    
    export default function MouseTracker() {
    
      // using our custom hook
     const [mouseX, mouseY] = useMouseLocation();
    
      return (
        <div>
          mouseX: {mouseX}, mouseY: {mouseY}
        </div>
      );
    }
    
    それは一種の「ユレーカ」瞬間です!じゃない?
    しかし、落ち着いて、反応フックの賛美を歌う前に、我々が何を認識しなければならないかについて見ましょう.

    フックの規則
  • だけはトップレベル
  • でフックを呼びます
  • クラスクラス
  • の内部でフックを使用できません
    これらの規則を説明することはこの記事の範囲を超えている.あなたが好奇心が強いならば、私はRedi docsとRdi Yardleyによってこのarticleを読むことを勧めます.
    また、これらの2つの規則を実施するeslint-plugin-react-hooksと呼ばれるESPINTプラグインをリリースしました.実行することでプロジェクトに追加できます.
    # npm 
    npm install eslint-plugin-react-hooks --save-dev
    
    # yarn 
    yarn add eslint-plugin-react-hooks --dev
    
    この記事は、2018年12月のReactsydney meetupの私の一部でした.私はこの記事をあなたが反応フックを試して興味を持っている願っています.私は非常に有望に見えると反応反応を現在使用する方法を変更する可能性がある反応roadmapについては非常に興奮している.
    このlinkではソースコードとデモを見つけることができます.
    あなたが記事が好きであるならば❤️ きっと私を笑顔にする😀. より多くの来ています.