深入り浅出React Hook s

15763 ワード

生放送再生リンク:雲栖コミュニティ(@x-cold)
React Hook sは何ですか?
Hooksは名前の通り、文字通りReactフックの概念です.caseを通して、私たちはReact Hook sに対してまず第一印象を持っています.
今カウンタのセットを実現すると仮定します.コンポーネント化の方法を使用すると、私たちは、ステートメントstate、カウンタの作成方法など、より多くのことを行う必要があります.また、Javascriptのクラスの概念、thisコンテキストの指向など、より多くの理解が必要です.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Counter extends React.Component {
  state = {
      count: 0
  }

  countUp = () => {
    const { count } = this.state;
      this.setState({ count: count + 1 });
  }
  
  countDown = () => {
    const { count } = this.state;
      this.setState({ count: count - 1 });
  }
  
  render() {
    const { count } = this.state;
      return (
      

{count}

) } } ReactDOM.render(, document.getElementById('root'));
React Hook sを使って、私達はこのように書くことができます.
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    

{count}

) } ReactDOM.render(, document.getElementById('root'));
上記の例では、React Hook sは、単純な関数式(FP)のプログラムスタイルを提供し、純関数コンポーネントと制御可能なデータストリームを介して、UIへの状態の相互作用(MVVVM)を実現することが明らかとなっている.
Hooks API
  • Baic Hook s
  • useState
  • useEffect
  • useContext
  • Additional Hook s
  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue
  • use State
    useStateは最も基本的なAPIであり、その初期値は関数実行毎に新しい値を得ることができる.
    import React, { useState } from 'react';
    import ReactDOM from 'react-dom';
    
    function Counter() {
      const [count, setCount] = useState(0);
      return (
        

    {count}

    ) } ReactDOM.render(, document.getElementById('root'));
    注意したいのは、useStateで得られた状態countは、Counterコンポーネントの中での表現が定数であり、一回ごとにset Countによって修正された後、再度useStateによって新しい定数が得られます.
    useReducer
    useReducerとuseStateはほぼ同じで、外付けreducer(大域)が必要であり、このように複数の状態を同時に制御することができる.詳しく見てみると、実はreduxのデータフローの概念に非常に近いです.
    import { useState, useReducer } from 'react';
    import ReactDOM from 'react-dom';
    
    function reducer(state, action) {
      switch (action.type) {
        case 'up':
          return { count: state.count + 1 };
        case 'down':
          return { count: state.count - 1 };
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, { count: 1 })
      return (
        
    {state.count}
    ); } ReactDOM.render(, document.getElementById('root'));
    useeffect
    一つの重要なHooks APIは名前の通り、useeffectは様々な状態の変化による副作用を処理するために使われています.つまり、特定の時点でしか実行できない論理です.
    import { useState, useEffect } from 'react';
    import ReactDOM from 'react-dom';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      // => componentDidMount/componentDidUpdate
      useEffect(() => {
        // update 
        document.title = `You clicked ${count} times`;
        // => componentWillUnMount
        return function cleanup() {
            document.title = 'app';
        }
      }, [count]);
    
      return (
        

    You clicked {count} times

    ); } ReactDOM.render(, document.getElementById('root'));
    use Memo
    useMemoは主にレンダリングプロセスの最適化に用いられ、2つのパラメータは順次計算関数(通常はコンポーネント関数)と依存状態リストであり、依存する状態が変化すると計算関数の実行がトリガされる.依存性が指定されていない場合は、レンダリングプロセス毎に計算関数が実行されます.
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
    import { useState, useMemo } from 'react';
    import ReactDOM from 'react-dom';
    
    function Time() {
        return 

    {Date.now()}

    ; } function Counter() { const [count, setCount] = useState(0); const memoizedChildComponent = useMemo((count) => { return
    useContext
    contextは外部createであり、内部useのstateは、グローバル変数との違いは、複数のコンポーネントが同時にuseConteextであれば、これらのコンポーネントはrerenderになります.複数のコンポーネントが同時にuseStateと同じグローバル変数であれば、set Stateの現在のコンポーネントrerenderをトリガするだけです.
    例-useContextを使用していません.
    import { useState, useContext, createContext } from 'react';
    import ReactDOM from 'react-dom';
    
    // 1.    createContext      
    const UserContext = new createContext();
    
    // 2.    Provider
    const UserProvider = props => {
      let [username, handleChangeUsername] = useState('');
      return (
        
          {props.children}
        
      );
    };
    
    // 3.    Consumer
    const UserConsumer = UserContext.Consumer;
    
    // 4.    Consumer     
    const Pannel = () => (
      
        {({ username, handleChangeUsername }) => (
          
    user: {username}
    handleChangeUsername(e.target.value)} />
    )}
    ); const Form = () => ; const App = () => (
    ); ReactDOM.render(, document.getElementById('root'));
    例→ useContextを使う
    import { useState, useContext, createContext } from 'react';
    import ReactDOM from 'react-dom';
    
    // 1.    createContext      
    const UserContext = new createContext();
    
    // 2.    Provider
    const UserProvider = props => {
      let [username, handleChangeUsername] = useState('');
      return (
        
          {props.children}
        
      );
    };
    
    const Pannel = () => {
      const { username, handleChangeUsername } = useContext(UserContext); // 3.    Context
      return (
        
    user: {username}
    handleChangeUsername(e.target.value)} />
    ); }; const Form = () => ; const App = () => (
    ); ReactDOM.render(, document.getElementById('root'));
    useRef
    useRefは、転送パラメータとして初期化されたcurrent属性の可変refオブジェクトを返します.返したオブジェクトは、コンポーネント全体のライフサイクルを継続します.事実上 useRefは非常に有用なAPIであり、多くの場合、私たちはいくつかの変更されたものを保存する必要があります.
    function TextInputWithFocusButton() {
      const inputEl = useRef(null);
      const onButtonClick = () => {
        // `current` points to the mounted text input element
        inputEl.current.focus();
      };
      return (
        <>
          
          
        >
      );
    }
    React状態共有方案
    状態共有といえば、最も簡単で直接的な方法は、プロpsを介して段階的に状態を伝達することであり、このような方法はコンポーネントの親子関係に結合され、コンポーネントの入れ子構造が変化すると、コードを再作成する必要があり、維持コストが非常に高い.時間が経つにつれて、公式は状態共有とコード多重化の問題を解決するための様々なスキームを発表した.
    ミxins
    Reactでは、CreateClassで作成したコンポーネントだけがmixinsを使用できます.このような高結合、依存は制御しにくく、複雑度の高い方法はES 6の波とともに次第に歴史舞台からフェードアウトしてきた.
    HOC
    高次のコンポーネントは関数プログラミングから由来しており、Reactのコンポーネントは関数(クラス)とも見なされるので、HOCの方式によってコード多重化が天生的に実現できる.プロパティエージェントと逆継承によって実現することができ、HOCはレンダリングの結果を操作しやすく、コンポーネントのprops/stateを操作することもでき、複雑なコード論理多重を容易に行うことができる.
    import React from 'react';
    import PropTypes from 'prop-types';
    
    //     
    class Show extends React.Component {
      static propTypes = {
        children: PropTypes.element,
        visible: PropTypes.bool,
      };
    
      render() {
        const { visible, children } = this.props;
        return visible ? children : null;
      }
    }
    
    //     
    function Show2(WrappedComponent) {
      return class extends WrappedComponent {
        render() {
          if (this.props.visible === false) {
            return null;
          } else {
            return super.render();
          }
        }
      }
    }
    
    function App() {
        return (
           0.5}>hello
      );
    }
    Reduxにおける状態多重化は典型的なHOCの実現であり、composeを通じて目的のコンポーネントにデータを組み立てることができます.もちろん、装飾器によっても処理することができます.
    import React from 'react';
    import { connect } from 'react-redux';
    
    // use decorator
    @connect(state => ({ name: state.user.name }))
    class App extends React.Component{
      render() {
            return 
    hello, {this.props.name}
    } } // use compose connect((state) => ({ name: state.user.name }))(App);
     
    Render Props
    renderPropsは、HOCのスキームよりも、render Propsが既存のコンポーネント階層構造を保護することができるということが明らかになった.
    import React from 'react';
    import ReactDOM from 'react-dom';
    import PropTypes from 'prop-types';
    
    //   HOC   ,         render prop           
    class Mouse extends React.Component {
      static propTypes = {
        render: PropTypes.func.isRequired
      }
    
      state = { x: 0, y: 0 };
    
      handleMouseMove = (event) => {
        this.setState({
          x: event.clientX,
          y: event.clientY
        });
      }
    
      render() {
        return (
          
    {this.props.render(this.state)}
    ); } } function App() { return (
    ( // render prop state

    The mouse position is ({x}, {y})

    )}/>
    ); } ReactDOM.render(, document.getElementById('root'));
    Hooks
    Hooks APIとReact内蔵のConteextを組み合わせることによって、前の例からHookを介してコンポーネント間の状態共有がより明確で簡単であることが分かります.
    React Hook sデザイン理念
    基本原理
    function FunctionalComponent () {
      const [state1, setState1] = useState(1);
      const [state2, setState2] = useState(2);
      const [state3, setState3] = useState(3);
    }
    {
      memoizedState: 'foo',
      next: {
        memoizedState: 'bar',
        next: {
          memoizedState: 'bar',
          next: null
        }
      }
    }
    関数的貫徹
    capture props
    関数コンポーネントは生まれつきpropsをサポートしていますが、基本的な使い方はclassコンポーネントと大差ないです.注意すべき二つの違いは:
  • classコンポーネントpropsは、thisコンテキストでマウントされ、関数コンポーネントは、モダリティによって入力される.
  • マウント位置の違いにより、classコンポーネントの中でthisが変化したら、this.propsも変化します.関数のコンポーネントの中でpropsはずっと可変ではないので、それを守ります. capture value 原則として、Hooksもこの原則に従います.
  • 一例を通してcapture valueを理解して、useRefによって回避できます. capture valueは、useRefが可変だからです.
    state
    クラスコンポーネント
    関数コンポーネント
    作成状態
    this.state={}
    useState、useReducer
    状態を変更
    this.set State()
    set function
    更新の仕組み
    非同期更新、複数の修正を前の状態に統合し、コピーを作成します.
    更新を同期して、直接目標状態に変更します.
    状態管理
    一つのstate集中中国語管理複数の状態
    複数のstateは、useReducerによる状態統合(手動)が可能です.
    パフォーマンス
    高さ
    もし useState初期化状態は非常に複雑な計算で得られます.関数の宣言方式を使ってください.そうでないとレンダリングは毎回繰り返し実行されます.
    ライフサイクル
  • component DidMount/component DidUpdate/componentWillUnMount
  • useeffectはレンダリングのたびに呼び出されます.少し包装すればこれらのライフサイクルとして使えます.
  • shuldComponentUpdate
  • 一般的にコンポーネントの性能を最適化する時、単一のコンポーネントのレンダリング回数を減らすために、純粋なコンポーネントを優先的に採用します.
    class Button extends React.PureComponent {}
    React HooksではuseMemoの代わりに、いくつかのデータが変化したときだけコンポーネントをレンダリングすることができます. sharlowEqualのショルドComponentUpdate.
    強制レンダリング forceUpdate
    デフォルトでは、状態を変更するたびに再レンダリングが行われますので、使わないset関数でforceUpdateとすることができます.
    const forceUpdate = () => useState(0)[1];
    原理を実現する
    Hooksに基づいて、Hooksを強化します.
    コンビネーションパンチをください.
    各Hooks APIは純関数の概念であるため、入力と出力(output)にもっと関心を持つようになり、より良い機能を組み立てることによって、異なる特性の基礎Hooks APIを組み合わせて、新しい特性を持つHooksを作成することができます.
  • useState保守コンポーネント状態
  • useEffect処理副作用
  • useContext provider更新の変化を監督する
  • useDidMount
    import { useEffect } from 'react';
    
    const useDidMount = fn => useEffect(() => fn && fn(), []);
    
    export default useDidMount;
    useDidUpdate
    import { useEffect, useRef } from 'react';
    
    const useDidUpdate = (fn, conditions) => {
      const didMoutRef = useRef(false);
      useEffect(() => {
        if (!didMoutRef.current) {
          didMoutRef.current = true;
          return;
        }
        // Cleanup effects when fn returns a function
        return fn && fn();
      }, conditions);
    };
    
    export default useDidUpdate
    useWillUnmount
    useEffectについて言及しましたが、cleanup関数を返すことができます.コンポーネントはマウントをキャンセルする時にこのcleanup関数を実行します. useWillUnmountも簡単に実現できます.
    import { useEffect } from 'react';
    
    const useWillUnmount = fn => useEffect(() => () => fn && fn(), []);
    
    export default useWillUnmount;
    useHover
    // lib/onHover.js
    import { useState } from 'react';
    
    const useHover = () => {
      const [hovered, set] = useState(false);
      return {
        hovered,
        bind: {
          onMouseEnter: () => set(true),
          onMouseLeave: () => set(false),
        },
      };
    };
    
    export default useHover;
    import { useHover } from './lib/onHover.js';
    
    function Hover() {
      const { hovered, bind } = useHover();
      return (
        
    hovered: {String(hovered)}
    ); }
    use Field
    // lib/useField.js
    
    import { useState } from 'react';
    
    const useField = (initial) => {
      const [value, set] = useState(initial);
    
      return {
        value,
        set,
        reset: () => set(initial),
        bind: {
          value,
          onChange: e => set(e.target.value),
        },
      };
    }
    
    export default useField;
    import { useField } from 'lib/useField';
    
    function Input {
      const { value, bind } = useField('Type Here...');
    
      return (
        
    input text: {value}
    ); } function Select() { const { value, bind } = useField('apple') return (
    selected: {value}
    ); }
    注意事項
  • Hookの使用範囲:関数式のReactコンポーネント中、カスタムHook関数内;
  • Hookは関数の一番外側に書かなければなりません.毎回 useStateはその下付きを変えます.Reactはその順序によって状態を更新します.
  • レンダリングのたびにHook APIが実行されますが、発生した状態は常に一定量です.
  • おわりに
    React Hooksは状態管理のために新たな可能性を提供しています.内部の状態を追加的に維持する必要がありますが、レダProps/HOCなどの複雑な方法で状態管理の問題を処理することは避けられます.Hooksのメリットは以下の通りです.
  • より細かい粒度のコード多重化は、過剰な副作用を生じさせない.
  • 関数式プログラミングスタイルは、コードがより簡潔であり、同時に使用しきい値を低減し、理解することができます.
  • コンポーネントネスト層数を減らす
  • コンポーネントのデータの流れがより鮮明になりました.
    実際には、さまざまなシーンをカスタマイズすることによって、私達のアプリケーションをより便利で簡潔にすることができます.コンポーネントの階層構造も完璧に保証できます.また、このような楽しい関数式プログラミングスタイルがあります.HooksはReact 16.8.0バージョンで正式に安定版を発表しました.今から使い始めましょう.
    参考資料
  • Hooks-reference
  • react-hoks-lib
  • 【React深さ】MixinからHOCまでHook
  • に行きます.
  • How Aree Function Components Different from Class?
  • under-the-Hook-off-reacts-Hook s-system
  • ソースを読んだ後、React Hook sはどうやって実現されたのですか?