半年以上前にReactのContextとHooksで日本語のふりがな入力を支援するコンポーネント書いた - valid,invalidという記事を書いたが、このときは何もわかってなかったということがわかった。
長々とContext
, Provider
, useReducer
あたりのことを書きましたが、同じことは以下の十数行のcustom hookで実現できた。
import historykana from 'historykana';
export const useKana = (fieldNames: string[]) => {
const [history, setHistory] = useState({});
const [kana, setKana] = useState({});
const setKanaSource = ({ fieldName, inputtedValue }) => {
const newHistory = inputtedValue ? [...history[fieldName], inputtedValue] : [];
setHistory({ ...history, [fieldName]: newHistory });
setKana({ ...kana, [fieldName]: historykana(newHistory) });
};
return { kana, setKanaSource };
};
state
を更新するロジックは大した複雑性もないのでuseReducer
は不要。state
を更新するにはdispatcher
相当のcallback関数を自前で書いてユーザーに提供すればよし。
ライブラリを使う側の気持ちとしても今だったらContext
じゃなくふつうにcustom hookを使いたいと思う。
import React from 'react';
import ReactDOM from 'react-dom';
import { useKana } from 'react-use-kana';
const App = () => {
const { kana: { lastNameKana }, setKanaSource } = useKana(['lastNameKana']);
return (
<>
<input
type="text"
onChange={e =>
setKanaSource({ fieldName: 'lastNameKana', inputtedValue: e.target.value })
}
/>
<input type="text" value={lastNameKana} />
</>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
Test for React Hooks
React hooksのテストってどうやって書くのかな?と思ってHooks FAQ – Reactを見ると@testing-library/react-hooks
を見つけたので試してみた。今回はJestと併用。
基本的にはReact Testing Libraryと類似のAPI(act
とか)が使える…と思ったらreact-test-renderer
に依存しており、そこからexportされているだけのようだった。
React Componentのテストのときと同様にact
にはユーザーインタラクションのいち単位を記述する。今回は一文字ずつの入力をインタラクションとして定義したく、ループ内でact
を呼ぶようなテストになった
import { renderHook, act } from '@testing-library/react-hooks';
import { useKana } from '../useKana';
test('returns kana based on user input', () => {
const { result } = renderHook(() =>
useKana(['lastNameKana', 'firstNameKana']),
);
expect(result.current.kana).toEqual({
lastNameKana: '',
firstNameKana: '',
});
[
'や',
'やm',
'やま',
'やまd',
'やまだ',
'山田',
].forEach(value => {
act(() => {
result.current.setKanaSource({
fieldName: 'lastNameKana',
inputtedValue: value,
});
});
});
expect(result.current.kana).toEqual({
lastNameKana: 'やまだ',
firstNameKana: '',
});
});
Publised
今回書いたreact-use-kana
はnpm registoryに公開し、react-kana-provider
はnpm deprecate
した。
github.com
www.npmjs.com