コンポーネントのコードにキーボードイベントの処理やスクロールイベントなど、 直接UIに関わらない処理が混じって、コードの可読性がしていたのでReact Custom Hooksを使用して整理しました。
React Custom Hooksはコンポーネントとロジックのコードを分離するために、よく使われます。 今回は複数の画面で使用してかつ、直接UIに関わらない部分についてCustom Hooksを使用してロジックの分離を行いました。
Pull Request
実装
ソフトウェアキーボードの表示判定
ペペロミアでは以下の画像のようにソフトウェアキーボードが表示中は右上にキーボードアイコンを表示させて アイコンをタッチするとキーボードを閉じる動作を実装しています。
これを愚直に実装すると、以下の感じになります。
import React, { useState, useEffect } from 'react'; import { View, Text, Keyboard } from 'react-native'; type Props = {} const Page: React.FC<Props> = (props) => { const [showKeyboard, setShow] = useState(false); useEffect(() => { Keyboard.addListener('keyboardDidShow', _keyboardDidShow); Keyboard.addListener('keyboardDidHide', _keyboardDidHide); return () => { Keyboard.removeListener('keyboardDidShow', _keyboardDidShow); Keyboard.removeListener('keyboardDidHide', _keyboardDidHide); }; }, []); const _keyboardDidShow = () => { setShow(true); }; const _keyboardDidHide = () => { setShow(false); }; return ( <View> { showKeyboard ? <Text>キーボード開いている</Text> : <Text>キーボード閉じている</Text>} </View> ) }
上記の例はキーボードのみの処理のみなので、まだ読めますが、実際の実装は以下みたいに読みづらいコードになっていました。
上記の処理からReact Custom Hooksを使用して分割すると以下みたいになります。
import { useState, useEffect } from 'react'; import { Keyboard } from 'react-native'; const useKeyboard = () => { const [showKeyboard, setShow] = useState(false); useEffect(() => { Keyboard.addListener('keyboardDidShow', _keyboardDidShow); Keyboard.addListener('keyboardDidHide', _keyboardDidHide); // cleanup function return () => { Keyboard.removeListener('keyboardDidShow', _keyboardDidShow); Keyboard.removeListener('keyboardDidHide', _keyboardDidHide); }; }, []); const _keyboardDidShow = () => { setShow(true); }; const _keyboardDidHide = () => { setShow(false); }; return { showKeyboard }; }; export default useKeyboard;
上記を利用してコーディングすると以下の通りになります。
import React from 'react'; import { View, Text } from 'react-native'; import useKeyboard from 'lib/useKeyboard'; type Props = {} const Page: React.FC<Props> = (props) => { const { showKeyboard } = useKeyboard(); return ( <View> { showKeyboard ? <Text>キーボード開いている</Text> : <Text>キーボード閉じている</Text>} </View> ) }
だいぶスッキリしましたね。 ※実際のコードは、こんな感じになりました。
https://github.com/wheatandcat/Peperomia/blob/master/src/components/templates/CreatePlan/Page.tsx
他にも以下をReact Custom Hooksで実装しました。
スクロール位置を取得
import { useState } from 'react'; import { NativeSyntheticEvent, TextInputScrollEventData, Platform, StatusBar, } from 'react-native'; import { getStatusBarHeight } from 'react-native-status-bar-height'; const top = Platform.OS === 'android' ? StatusBar.currentHeight : getStatusBarHeight(); const useScroll = (offsetY: number = 84) => { const [scrollBelowTarget, setScrollBelowTarget] = useState(true); const onScroll = (e: NativeSyntheticEvent<TextInputScrollEventData>) => { const offsetScrollY = offsetY + (top || 0); if (e.nativeEvent.contentOffset.y >= offsetScrollY && scrollBelowTarget) { setScrollBelowTarget(false); } if (e.nativeEvent.contentOffset.y < offsetScrollY && !scrollBelowTarget) { setScrollBelowTarget(true); } }; return { onScroll, scrollBelowTarget, }; }; export default useScroll;
スケジュールのタイトル入力からサジェストを管理
■ src/hooks/useItemSuggest.tsx
import { useCallback, useState } from 'react'; import { useItems } from 'containers/Items'; import { SuggestItem, uniqueSuggests } from 'lib/suggest'; const useItemSuggest = () => { const { items, itemDetails } = useItems(); const [suggestList, setSuggest] = useState<SuggestItem[]>([]); const getSuggestList = useCallback((): SuggestItem[] => { const suggestList1 = (items || []).map((item) => ({ title: item.title, kind: item.kind, })); const suggestList2 = (itemDetails || []).map((itemDetail) => ({ title: itemDetail.title, kind: itemDetail.kind, })); const r = [...suggestList1, ...suggestList2]; return r; }, [items, itemDetails]); const setSuggestList = useCallback( (title: string) => { const r = uniqueSuggests(getSuggestList()) .filter((item) => { if (!title) { return false; } return item.title.includes(title); }) .slice(0, 8); setSuggest(r); }, [getSuggestList] ); return { setSuggestList, suggestList, }; }; export default useItemSuggest;
最後に
まだReact Custom Hooksでコードをシンプル出来る箇所がありそうなので、ちょいちょいリファクタリング進めて行く予定