こんにちは。フロントエンドエンジニアの渡邉です。 最近フロントエンド界隈で盛り上がっているRecoilについて学びました。
本記事は自分のRecoil入門のついでに記事にしたので、初級者向けになっています。
目次
- Recoilとは
- 使ってみる
- API Referenceを読む
- 参考サイト
Recoilとは
Fecebookが新しく発表したのReactの状態管理ライブラリです。 公式ドキュメント
使ってみる
何からやったらいいか分からない人もいるかも知れないので自分の学習手順を紹介しつつ実際に触っていきます。
- 公式ドキュメントのGetting Started & BasicTutorialをやる
- Recoilについて書かれている記事を読む
- 公式ドキュメントのAPI Referenceを読む
みたいな感じで学習しました。
Getting Startedでは入力した文字を出力するのとその入力された文字数カウントを出力するアプリを作っていきます。
この記事でもGetting Startedを一緒にやっていきます。
まずはアプリケーション作成・移動
npx create-react-app my-app cd my-app
recoil install
npm install recoil or yarn add recoil
RecoilRoot
recoilを使うにはルートコンポーネントをRecoilRoot
でくくる
App.js
import React from 'react'; import { RecoilRoot, } from 'recoil'; function App() { return ( <RecoilRoot> <CharacterCounter /> </RecoilRoot> ); }
CharacterCounterでは入力フォームと入力された文字を出力するTextInput
と入力された文字数を出力するCharacterCount
を呼び出しています。
CharacterCounter.js
import React from 'react'; import TextInput from './TextInput'; import CharacterCount from './CharacterCount'; function CharacterCounter() { return ( <div> <TextInput /> <CharacterCount /> </div> ); } export default CharacterCounter
Recoilでは複数コンポーネントで共有されるステートはatomと呼ばれます。 今回はTextInputで扱うstateをCharacterCountでも使いたいので、atom関数を用いて作ります。 atomの値を読み取るコンポーネントを暗黙的にサブスクライブされるので、atomに更新が入るとそのatomを使用しているコンポーネント全てに再レンダリングが走ります。
export const textState = atom({ key: 'textState', default: '' })
atomを作るにはkey(一意のID)とデフォルト値を設定します。
更に読み取り書き込みしたい場合は、useRecoilState
を使います。
useRecoilState
はuseStateみたいな感覚で使えます。
デフォルト値がuseStateと違い、先程定義したatomを引数に受け取ります。
TextInput.js
import React from 'react'; import { atom, useRecoilState } from 'recoil'; export const textState = atom({ key: 'textState', default: '' }) function TextInput() { const [text, setText] = useRecoilState(textState); const onChange = (event) => { setText(event.target.value); }; return ( <div> <input type="text" value={text} onChange={onChange} /> <br /> Echo: {text} </div> ); } export default TextInput
CharacterCountでは先程定義したtextState
を使ってその文字数をレンダーします。
atomの値から計算し、別の値として出すにはselector
関数を使います。
const characterCountState = selector({ key: 'characterCountState', get: ({get}) => { const text = get(textState) // getの引数にstateを渡す。 return text.length } })
Recoilではatom
とselector
を合わせてstateと呼びます。
どういうことかと言うと、先程使用したuseRecoilStateはatomだけではなくselectorに対しても使えます。
atomとselectorはグローバルな値を提供するという点で共通しています。
違いとしては、atomは自身が値を持っており、selectorはatomから算出された値というところです。
atomと同様にuseRecoilState
を使い値を取り出せます。
ですが、今回必要なのは値の読み取りだけなのでその場合にはuseRecoilValue
を使います。
逆に書き込みだけしたい場合にはuseSetRecoilState
を使います。
上記3つのhooksをまとめると
const hogeState = atom({ key: 'hogeState', default: '' }) // useRecoilState const [hoge, setHoge] = useRecoilState(hogeState) // useRecoilValue const hoge = useRecoilValue(hogeState) // useSetRecoilState const setHoge = useSetRecoilState(hogeState) // useRecoilStateで値だけ取る場合 const [hoge] = useRecoilState(hogeState) // useRecoilStateで更新関数だけ取る場合 const [, setHoge] = useRecoilState(hogeState)
今回はuseRecoilValue
を使って値を読み取ります。
CharacterCount.js
import React from 'react'; import { selector, useRecoilValue } from 'recoil'; import { textState } from './TextInput' const characterCountState = selector({ key: 'characterCountState', get: ({get}) => { const text = get(textState) return text.length } }) function CharacterCount() { const count = useRecoilValue(characterCountState) return ( <p>character count: {count}</p> ); } export default CharacterCount
これでGetting Startedのアプリは完成です。
API Referenceを読む
Getting Startedだけやっても基本的なことしかわからないので、Getting Startedを終えたらAPI Referenceを読みました。
この記事ではRecoilで基礎的な概念のatom
とselector
を紹介します。
atom()
atom(options)
selector()
selector(options)
options
key
: atomと同じくselectorを識別する一意の文字列get
: 引数からgetを受け取るget関数(ややこしすぎる)get
: get関数から受け取ったgetにstateを渡すことでそのstate値を用いることができる。
getにわたしているstateは暗黙的にリストに追加されるのでstateが更新入るとselectorは再評価されます
set?
: このプロパティが設定されると書込み可能なselectorになります。getとsetをパラメータとしてオブジェクトを渡す関数
setはReference読んだだけだとちゃんと理解できなかったので簡易なコードを見ながら説明します。
流れを先に記載しておくのでコードと照らし合わせながら読んでみてください。
sCount
初期値0
setSCount(sCount + 1)
実行- set関数で受け取った他のatomを書き込むための(今回の場合はnomalCountに書き込む)set関数と、newValue(
1
)を受け取る - atomに書き込むためのset関数にnomalCountを渡し、newValueを10倍にして返す
- nomalCountが更新されたのでspecialCountのgetが実行されnomalCountの値を返す
- sCountが更新され、描画
import React from 'react' import {atom, selector, useRecoilState} from 'recoil'; const nomalCount = atom({ key: 'nomalCount', default: 0, }); const specialCount = selector({ key: 'specialCount', get: ({get}) => get(nomalCount), set: ({set}, newValue) => set(nomalCount, newValue * 10), }); export default function SpecialCounter() { const [sCount, setSCount] = useRecoilState(specialCount); const addSCount = () => setSCount(sCount + 1); return ( <div> <p>specialCount: {sCount}</p> <button onClick={addSCOunt}>add special</button> </div> ); }
参考サイト
https://blog.uhy.ooo/entry/2020-05-16/recoil-first-impression/
まとめ
普段自分はReduxを使って状態管理をしています。ReduxとRecoilでは、stateの持ち方が異なることをがわかりました。
Reduxでは、ロジックや画面ごとにreducerを持ち、combineReducers
を使って一つのreducerにまとめます。
stateを使う場合はuseSelector
を使ってstoreのstateから自分が必要なデータを取り出します。
一方Recoilはそもそも局所的にstateをもち、stateを利用するコンポーネント間でのみ共有します。
他にもRecoilではReduxみたいにstoreを作ってreducer作ってaction作ってみたいな作業がなくて、すぐに取り掛かれるのが個人的には嬉しかったです。
最後に、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひエンジニア採用サイトをご覧ください。