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を使用したい際は、 Connect
の HOC
を自作すれば簡単に実現可能になります。
余談
面白そうな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版
- 回転度?とか色々取れるみたい