コンポーネントのコードにキーボードイベントの処理やスクロールイベントなど、 直接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でコードをシンプル出来る箇所がありそうなので、ちょいちょいリファクタリング進めて行く予定