はじめに
Reduxって難しいですよね…。
でもアプリの規模によっては避けて通れない時もある。。
この記事はredux toolkitを使ってreduxをなるべく簡単に実装するための記事です。
React Nativeで実装していきます。
実現すること
- storeからstateを取ってきて文字列を画面に表示するだけの実装(payloadとかはいったんなし)
- ある程度規模が大きくなっても管理しやすい形にする
画面実装
ここら辺で実装されている画面をコピペするだけでOKです
HomeScreenとDetailScreenの2画面をnavigationで行き来するだけの画面です
React NativeでNavigationを使っていい感じに画面遷移する
ライブラリをインストールする
公式ドキュメントに記載されているライブラリに加えてreact-reduxもインストールしましょう
yarn add @reduxjs/toolkit
yarn add react-redux
yarn add redux
ちなみにどんなライブラリを入れたかというと
ライブラリ | 用途 |
---|---|
@reduxjs/toolkit | redux toolkit |
react-redux | ReactにReduxを組み込む方法を提供する。 |
redux | redux本体 |
一応pod installもやっておきましょう
cd ios && pod install && cd ..
react-native run-ios
これで下準備が完了!!
ディレクトリを用意する
src配下にreduxというディレクトリを用意し、その中にtodoとuserというディレクトリを用意しましょう
ファイルも用意する
todoの中にはtodoSlice.js
userの中にはuserSlice.jsを作成します。
これらがあとあと個別のstoreになります。(いったんまだ何も書かなくてOK)
createSliceを使って個別のstoreを作成
Redux Toolkit から提供される createSlice 関数を用いて個別のストアを作成します。
先ほど作成したtodoSlice.jsに処理を加えます。画面実装は以下をイメージ。
- userTodoというstateの初期値はnull
- setTodoを押した時に文字列にjoggingと入れる
- resetTodoを押した時に初期値(null)に戻る
todoSlice.js
import {createSlice} from '@reduxjs/toolkit';
// Stateの初期状態
const todoInitialState = {
userTodo: null,
};
// Sliceを生成し、exportする
export const todoSlice = createSlice({
name: 'todo',
initialState: todoInitialState,
reducers: {
setTodo: state => {
state.userTodo = 'jogging';
},
resetTodo: state => {
state.userTodo = todoInitialState.userTodo;
},
},
});
// Action Creatorsをエクスポート
export const {setTodo, resetTodo} = todoSlice.actions;
次にuserSlice.jsにも処理を加えます。
画面実装は以下をイメージしてます。
- userNameとuserEmailというstateの初期値はnull
- getInfoを押した時に文字列を入れる
- resetInfoを押した時に初期値(null)に戻る
userSlice.js
import {createSlice} from '@reduxjs/toolkit';
const userInitialState = {
userName: null,
userEmail: null,
};
export const userSlice = createSlice({
name: 'user',
initialState: userInitialState,
reducers: {
getInfo: state => {
state.userName = 'Tanaka Taro';
state.userEmail = 'example@gmail.com';
},
resetInfo: state => {
state.userName = userInitialState.userName;
state.userEmail = userInitialState.userEmail;
},
},
});
export const {getInfo, resetInfo} = userSlice.actions;
reducerを結合する
reduxディレクトリ配下にreducers.jsを作成し、
Reduxが提供する combineReducers 関数で各 Slice の Reducer を結合します。
reducers.js
import {combineReducers} from '@reduxjs/toolkit';
import {userSlice} from './user/userSlice';
import {todoSlice} from './todo/todoSlice';
export const rootReducer = combineReducers({
user: userSlice.reducer,
todo: todoSlice.reducer,
});
storeを生成する
reduxディレクトリ配下にstore.jsを作成し、先ほど結合したReducerを configureStore 関数に渡してstoreを生成します
store.js
import {configureStore} from '@reduxjs/toolkit';
import {rootReducer} from './reducers';
const store = configureStore({reducer: rootReducer});
export default store;
完成したstoreをアプリ内で使用する
react-redux から提供される Provider コンポーネントを使用します。
App.jsあたりに記述しましょう
App.js
import React from 'react';
import RootStackScreen from './src/navigations/';
import store from './src/redux/store';
import {Provider} from 'react-redux';
const App = () => {
return (
<Provider store={store}>
<RootStackScreen />
</Provider>
);
};
export default App;
画面ごとにstateを受け取る
ストアを参照して更新します。まず初めにHomeScreenにstateを受け取るための処理を書きます
そのためには useSelector と useDispatch というフックを使用します
HomeScreen.js
import React from 'react';
import {StyleSheet, View, Text, Button} from 'react-native';
import {getInfo, resetInfo} from '../redux/user/userSlice';
// フックをimport
import {useSelector, useDispatch} from 'react-redux';
const HomeScreen = ({navigation}) => {
const dispatch = useDispatch();
// userというstateを参照する
const user = useSelector(state => state.user);
return (
<View style={styles.container}>
<Text>私の名前は{user.userName}</Text>
<Text>メールアドレスは{user.userEmail}</Text>
<Button
title="セットする"
onPress={() => {
// dispatch関数を実行することで該当のreducerが作動する
dispatch(getInfo());
}}
/>
<Button
title="リセットする"
onPress={() => {
dispatch(resetInfo());
}}
/>
<Button
title="画面遷移する"
onPress={() => {
navigation.navigate('Detail');
}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default HomeScreen;
セットするというボタンを押すとTanaka Taroとexample@gmail.comが入って、
リセットするというボタンを押すと空文字(null)になるはずです。
お次はDetailScreenでtodoのstateをもらいましょう
同様に useSelector と useDispatch を使います。
DetailScreen.js
import React from 'react';
import {StyleSheet, View, Text, Button} from 'react-native';
import {setTodo, resetTodo} from '../redux/todo/todoSlice';
import {useSelector, useDispatch} from 'react-redux';
const DetailScreen = ({navigation}) => {
const dispatch = useDispatch();
const todo = useSelector(state => state.todo);
return (
<View style={styles.container}>
<Text>明日のTodoは{todo.userTodo}</Text>
<Button
title="セットする"
onPress={() => {
dispatch(setTodo());
}}
/>
<Button
title="リセットする"
onPress={() => {
dispatch(resetTodo());
}}
/>
<Button
title="Home画面に遷移する"
onPress={() => {
navigation.navigate('Home');
}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default DetailScreen;
セットするというボタンを押すとjoggingが入って、
リセットするというボタンを押すと空文字(null)になるはずです。
簡単にですがこれでstoreからstateを受け取ることができました!
最後に
やはりReduxを使うと記述量がどうしても多くなってしまい、複雑化してしまいますよね。
それをなるべく簡単にするのがredux-toolkitです。
ここまで紹介した実装であれば、扱わなければならないsliceが増えてもディレクトリごとにファイルを分けて、reducers.jsの中でimportすれば何かと管理しやすくなるかと思います。
参考
https://www.hypertextcandy.com/learn-react-redux-with-hooks-and-redux-starter-kit
https://future-architect.github.io/articles/20200429/