Reactにおける状態管理
Reactの強みであるStateについて、その機能のおかげでリアルタイムのフォーム処理を行うことができたり、プログラムの自由度が増したが、その一方で変数が複数になりタコ足回線のようなPropsの渡し合いでプログラムの複雑さが増した。
そのため必要となったのが土台の部分でStateを管理する機能である。有名なものにはReduxやRecoilがある。
Recoilによる状態管理
アプリケーションの中で使うStateはAtomという保管場所で管理を行う(Reduxで言うStoreの概念)。
srcフォルダの下にatomsフォルダを作り、その下にatomファイルを置く。
import { atom } from 'recoil'
export const textState = atom({
key: 'textState', // アプリケーション全体で使うキー
default: '', // 初期値
});
// 同じファイルに複数のStateを設定することも可能
export const numState = atom({
key: 'numState',
default: 0,
})
この値の呼び出し方に行く前に、最初にRecoilでStateを扱う場合に適用範囲を定めるため、useContextのようにタグで囲う。
import { RecoilRoot } from 'recoil'
import { TextInput } from './components/TextInput'
import { CharacterCount } from './components/CharacterCount'
class App extends React.Component {
render(){
return (
<RecoilRoot>
<TextInput />
<CharacterCount />
</RecoilRoot>
)
}
}
export default App;
実際にRecoilを使った処理について
import { useRecoilState } from 'recoil'
import { textState, numState } from '../atoms/atomState'
export const TextInput = () => {
const [text, setText] = useRecoilState(textState);
const [num, setNum] = useRecoilState(numState);
// 入力値でAtomを更新
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
}
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setNum(Number(e.target.value));
}
return(
<div>
<input type="text" onChange={handleChange} />
<select value={num} onChange={handleSelectChange}>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
{/*Reactにおいて<br>は閉じてない判定されるため使わないように*/}
<br />
出力: {text}
出力(セレクト): {num}
</div>
);
};
AtomからStateを引っ張ってきてuseRecoilState関数に引き渡すだけで、簡単にStateとセット関数を取ってこれる。
Selectorを使ってみる
RecoilではAtomに変数を設定するだけではなく、Selectorという機能を使うことでAtomに格納しているStateを編集して返す、より自由度の高い状態管理を実現できる。
まずAtom同様にsrcフォルダの下にselectorフォルダを作成し、selectorファイルを置く。
import { selector } from 'recoil'
import { textState, numState } from '../atoms/atomState'
export const charCountState = selector({
key: 'charCountState',
//Atomの値を加工して返す関数を設定、GetRecoilValue型のgetを引数
get: ({get}) => {
const text = get(textState)
//加工した値を返す
return text.length
}
})
export const sumNumState = selector({
key: 'sumNumState',
get: ({get}) => {
const text = get(textState);
const num = get(numState);
//加工した値を返す
return text.length * num
}
})
Atomと似ているが、getの部分で計算処理などをさせることができる。
そして、それを機能としてアプリケーションの中に実装する部分については
import { useRecoilValue } from 'recoil'
import { charCountState, sumNumState } from '../selectors/selectorState'
export const CharacterCount = () => {
// stateとset関数を取得するuseRecoilState関数と違いstate取得のみを行う
const count = useRecoilValue(charCountState)
const sum = useRecoilValue(sumNumState)
return (
<div>
<p>文字数:{count}</p>
<p>合計:{sum}</p>
</div>
);
}
このようにRecoilを使えばuseContextを使うよりも簡単にクラス間やページ間の値の受け渡しができるため、Reactの開発において絶対不可欠の機能だと思われる。
同様の機能にReduxというものもあるが、こちらの方が主流ではあるがルールが多く構造も複雑になるため、大規模開発でもしない限りはRecoilの方が良いだろうと考える。