LoginSignup
25
21

More than 3 years have passed since last update.

React Hooksの解説

Last updated at Posted at 2019-07-02

ReactHooks

Hooks は既にあなたが知っている React のコンセプトを置き換えるものではありません。むしろ、フックはあなたが既に知っている props、state、コンテクスト、ref、ライフサイクルといったコンセプトに対してより直接的な API を提供するものです。後でお見せするように、フックによって、これらを組み合わせるパワフルな手段も得ることができます。

React からクラスを削除する予定はありません。

公式抜粋

hooksのルール

  • フックは関数のトップレベルのみで呼び出してください。ループや条件分岐やネストした関数の中でフックを呼び出さないでください。
  • フックは React の関数コンポーネントの内部のみで呼び出してください。通常の JavaScript 関数内では呼び出さないでください
    • カスタムフックの場合は例外

公式抜粋

個人的な意見

  • ページ全体の制御をする場合は従来のクラスコンポーネントを使用するべき
    • 込み入った事をしようとするとちょこちょこハマる
  • 便利ではあるが多用するとわけわからない事になるので、SFC( Stateless Functional Component )とコンポーネントの、間に位置する存在
  • アニメーションとかはこれのおかげで多少強くなった気がする。

useState

const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

useEffect

類似hooks

  • useLayoutEffect (コレに関してはほぼほぼ同じ)
  • useMemo
  • useCallback

ComponentDidMount

useEffect(() => {
  console.log('component did mount');
}, []);

ComponentWillUnMount

useEffect(() => {
  return  () => {
    console.log('component will unmount');
  };
}, []);

ノンブロッキングな命令であるため正確にはDidMountやWillMountとは挙動が異なる

useEffect系の解説

  • 第一引数に実行したい関数を渡す
    • returnに記述したものは、コンポーネントが破棄される際に呼ばれる
      • コネクションを明示的に切る必要のあるソケットだけの処理を外に記述できるメリットがある
  • 第二引数に変更がかかったさいに変更したい関数を渡す

useEffectとuseLayoutEffectの違い

上記で解説した通りノンブロッキングな命令に対し
useLayoutEffectは同期的に実行されるため、アニメーションなどに向いている(らしい)

useMemoとは

const memoHoge = useMemo(() => {
  return props.hoge
}[props.hoge]);

props.hogeの値が変わると、コールバックが使用され帰り値が変更される。

vueのcomputedのようなもの

useCallbackとは

const memorizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

中の値のcallbackを返却します。

const memorizedCallback = useMemo(() => {
  return () => {
    doSomething(a, b);
  }
}, [a, b]);

と同じようなもの

ハマりどころ

  • useStateと一緒に使用する際下記のように記述しないと、依存ステータスが更新されない
  const [count, setCount] = React.useState(0);

  const sampleCallback = React.useCallback(
    (event) => {
      console.log(event.target);
      setCount(count + 1);
    },
    [count] // ここがないとカウントが1以上進まなくなる
  );

useRef

ref要素を取得するための箱を提供する。

愚直にグローバルに書いても問題無いとは思うが、こちらのほうがスマートな印象がある。

const TextInputWithFocusButton = () => {
  const inputEl = useRef(null);
  const onButtonClick = useCallback((event) => {
    inputEl.current.focus(); // currentでrefの参照先が取れる
  }, [inputEl]);
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>テキストを入力</button>
    </>
  );
}

useReducer

  • useStateの代替品
    • 普通にReact-Reduxを簡単にしただけのもの
  • Stateが氾濫した際に有用
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  // const [state, dispatch] = useReducer(reducer, initialState, initialAction)
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

useContext

  • ここでStoreが簡単に作れるようになる
const value = useContext(MyContext);
  • React.createContext からの戻り値を受け取り、そのコンテクストの現在値をを返却する。
    • コンテクストの現在値は、ツリー内でこのフックを呼んだコンポーネントの直近にある の value の値によって決定されます。

Storeとかも簡単に作れるようになる

import ReactDOM from 'react-dom';
import React, { useReducer, useContext } from 'react';

// createContextを使用する
const context = React.createContext();

interface IState {
  count: number;
  user: {
    name: string;
  };
}

const initialState: IState = {
  count: 0,
  user: { name: 'araki' }
};

const reducer = (state: IState, action: any) => {
  switch (action.type) {
    case 'INCREMENT':
      return {
        ...state,
        count: state.count + 1
      };
    default:
      return state;
  }
};

// contextへ stateとdispatchをバインドする
const Provider = (props: { children: any }) => {
  const { children } = props;
  const [state, dispatch] = useReducer(reducer, initialState);

  return <context.Provider value={{ state, dispatch }}>{children}</context.Provider>;
};

const DispatchSample = () => {
  const { state, dispatch } = useContext(context);

  return (
    <div>
    <div>{state.count}</div>
      <button onClick={() => dispatch({ type: 'INCLEMENT' })}>
        increment
      </button>
    </div>
  )
}

const App = () => {
  return (
    <Provider>
      <DispatchSample />
    </Provider>
  );
};

ReactDOM.render(<App />, document.querySelector('#app'));

useContext は非常に強いなと感じましたが
ClassComponent で従来通りのStoreを使用したい際は、 ConnectHOC を自作すれば簡単に実現可能になります。

余談

面白そうなHooks製ライブラリ

react-vim

rhysd/react-vim-wasm: Vim editor empowers your React web application
Example of react-vim-wasm

  • Web上でVIMが使用できるらしい

react-use

streamich/react-use: React Hooks — 👍

  • スター数が結構多いHookライブラリ

    • かゆいところに手が届く感じの機能が多い
  • センサー系のHooks

    • ジオロケーション useGeolocation
    • モーションセンサー useMotion
    • ネットワークの状態 useNetwork
    • デバイスの画面の向きの状態 useOrientation
  • UI系のHooks

    • オーディオの再生とコントロール useAudio
    • ビデオのコントロール useVideo
    • ビデオのフルスクリーン useFullscreen
    • UI用の待機系命令 useWait
    • 音声入力 useSpeech
  • その他

    • 非同期依存注入 useAsync
    • 通信のリトライ useRetry
    • クリップボードへコピー useCopyToClipboard
    • ユーザーがページをリロードや閉じる際に警告を促す useBeforeUnload
    • bodyのスクロールをロックする(Modalとかのやつ) useLockBodyScroll
    • sessionStorageの管理 useSessionStorage

the-platform

palmerhq/the-platform: Web. Components. 😂

  • インタラクティブなデバイスの情報取得系命令が詰まったHooks
    • 回転度?とか色々取れるみたい useDeviceMotion
    • ジャイロスコープ?の情報取得 useDeviceOrientation
    • スクリプトの依存注入 useScript ※自分がパフォーアンスチューニングでやってたやつ
    • CSSの依存注入 useStylesheet 上記のCSS版
25
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
21