React Native

ReactNativeでRecoilを使ってState管理してみた。(Redux vs Recoil)

React Native
この記事は約12分で読めます。

こんにちは!

今回はRecoilを使ってState管理する方法を記述して行きます。
ReactNativeでログイン機能を実装してみた。
この記事のコードをベースにRecoilを実装して行きますので適宜参照してください。
今回実装した挙動は上記の記事と全く同じものになりますので、本記事では省きます。

Recoilとは

まずは公式ドキュメントをペタッと。

Recoilは機能し、Reactのように考えます。アプリにいくつか追加して、高速で柔軟な共有状態を取得します。

やはり翻訳だとわかりにくいですが、Reduxと同様にStateを管理するのに用います。
Reduxについてはこちらで解説してます。

ReduxとRecoilの違い

※私が学習した中での見解ですので、間違えてたら申し訳ございません。

ReduxにおけるState(Store)は 1つの巨大なオブジェクト である。
RecoilにおけるState(Atoms)は 1つの値を保持するState である。
日本語がおかしい気もしますが、こんな感じでしょうか。

Stateを更新する時、
Reduxは ActionをDispatchしてReducerという関数でStateをStoreに保存する。
Recoilは ReactHooksを介してAtomの値を変更する

ざっくりとすぎますがこんな感じでしょうか。

Recoilのインストール

ではプロジェクトを作成

react-native init RecoilSample

作成したらプロジェクト内でRecoilのインストール

npm install recoil

はい、以上です。

Recoilでログイン機能を実装

ReactNativeでログイン機能を実装してみた。
この記事ではReduxを使用したコードで、今回はRecoilを使用したコードです。
長々とコードを書いて行きますが、Recoilの部分以外はコメントも解説もしませんので上記記事を参照してください。

まずは今回管理するStateとして、ログインの際に使用する
・isSignedIn
・email
・password
を管理しますので、その準備をして行きます。

src/component/Recoil.js

import {atom, selector} from 'recoil';

// atom
export const inSignedAtom = atom({
  //キーを設定
  key: 'inSignedAtom',
  //初期値
  default: false,
});

export const emailAtom = atom({
  key: 'emailAtom',
  default: '',
});

export const passwordAtom = atom({
  key: 'passwordAtom',
  default: '',
});

// selector
export const isSignedSelector = selector({
  //キーを設定
  key: 'isSignedSelector',
  //どのAtomを参照するかを設定
  get: ({get}) => get(inSignedAtom),
});

export const emailSelector = selector({
  key: 'emailSelector',
  get: ({get}) => get(emailAtom),
});

export const passwordSelector = selector({
  key: 'passwordSelector',
  get: ({get}) => get(passwordAtom),
});

Reduxでは「Provider」で囲えば使えるようになりますが、Recoilでも同様に「RecoilRoot」で囲う必要があります。
App.js

import React from 'react';
import Router from './components/Router';
import {RecoilRoot} from 'recoil';

const App = () => {
  return (
    <RecoilRoot>
      <Router />
    <RecoilRoot>
  );
};

export default App;

画面遷移の処理を記載するRouter.js

src/component/Router.js

import React from 'react';
import {createDrawerNavigator} from '@react-navigation/drawer';
import {NavigationContainer} from '@react-navigation/native';
import SignUpScreen from '../screens/SignUpScreen';
import SignInScreen from '../screens/SignInScreen';
import SignOutScreen from '../screens/SignOutScreen';
import Home from '../screens/Home';
import {isSignedSelector} from '../component/Recoil';
import {useRecoilValue} from 'recoil';


const Drawer = createDrawerNavigator();

const Router = () => {
  //これでAtomの値を変数に代入
  const isSignedIn = useRecoilValue(isSignedSelector);
  return (
    <>
      {isSignedIn ? (
        <NavigationContainer>
          <Drawer.Navigator initialRouteName="Home">
            <Drawer.Screen name="Home" component={Home} />
            <Drawer.Screen name="SignOutScreen" component={SignOutScreen} />
          </Drawer.Navigator>
        </NavigationContainer>
      ) : (
        <NavigationContainer>
          <Drawer.Navigator initialRouteName="SignInScreen">
            <Drawer.Screen name="SignInScreen" component={SignInScreen} />
            <Drawer.Screen name="SignUpScreen" component={SignUpScreen} />
          </Drawer.Navigator>
        </NavigationContainer>
      )}
    </>
  );
};

export default Router;

次にサインイン画面のSignInScreen.js

src/screen/SignInScreen.js

import React, {useState} from 'react';
import {Button,View,Text,SafeAreaView,TextInput,Alert,} from 'react-native';
import firebase from '../firebase/config';
import {jpCheck,blankCheckEmail,blankCheckPassword,checkEmailFormat,} from '../utils/index';
import ErrorMessage from '../components/ErrorMessage';
import {emailAtom, passwordAtom, inSignedAtom} from '../component/Recoil';
import {useRecoilState} from 'recoil';

const SignInScreen = ({navigation}) => {

  //Inputで入力された文字がAtomで管理できます。useStateと似たような感じですね。
  const [isSignedIn, setIsSignedIn] = useRecoilState(inSignedAtom);
  const [email, setEmail] = useRecoilState(emailAtom);
  const [password, setPassword] = useRecoilState(passwordAtom);

  const signIn = (email, password) => {
    const isJapanese = jpCheck(email);
    const isBlankEmail = blankCheck(email);
    const isBlankPassword = blankCheck(password);
    const isFormatAddress = checkEmailFormat(email);
    if (isJapanese || isBlankEmail || isBlankPassword || isFormatAddress) {
      Alert.alert('入力に誤りがあります。正しく入力してください');
      return;
    } else {
      firebase
        .auth()
        .signInWithEmailAndPassword(email, password)
        .then((user) => {
          if (user) {
            //これだけでサインイン状態のStateを更新できるのでHomeへ遷移します
            setIsSignedIn(true);
            return;
          }
        })
        .catch((error) => {
          if (
            error.message ===
            'There is no user record corresponding to this identifier. The user may have been deleted.'
          ) {
            Alert.alert('アカウントが見つかりません');
          } else {
            Alert.alert('エラーです。');
          }
        });
    }
  };

  return (
    <SafeAreaView>
      <View>
        <Text>サインイン</Text>
      </View>
      <View>
        <View >
          <TextInput
            placeholder={'メールアドレス'}
            value={email}
            autoCapitalize="none"
            onChangeText={setEmail}
          />
        </View>
        <ErrorMessage email={email} typedText={email} />
        <View >
          <TextInput
            placeholder={'パスワード'}
            type={'password'}
            secureTextEntry={true}
            onChangeText={setPassword}
          />
        </View>
        <ErrorMessage password={password} typedText={password} />
        <View >
          <Button title="サインイン" onPress={() => signIn(email, password)} />
        </View>
        <View >
          <View >
            <Text>アカウントをお持ちでない方は下記よりご登録ください。</Text>
          </View>
        </View>
        <View>
          <Button
            title="新規登録はこちらから"
            onPress={() => navigation.navigate('SignUpScreen')}
          />
        </View>
      </View>
    </SafeAreaView>
  );
};

export default SignInScreen;

もしこのコードをそのまま使う方がいましたら、HomeやSignUpは
ReactNativeでログイン機能を実装してみた。や、
初心者がReduxToolkitを使ってみた。を参考にご自身でコーディングしてみてください!
おそらくほぼそのまま使えるのですぐできると思います。

感想

いかがでしたでしょうか。
実際にReduxの方が多く使われていますし、ReactでのState管理はとりあえずRedux感が否めませんが、
初心者の私でも割とすんなり実装できたのを考えるとすごく扱いやすい気がします。

以上です!