主なリソース・動作環境
- React-Native with managed Expo env.
- Redux Toolkit –
createSlice
、configureStore
、Redux-thunk - React Hooks –
useReducer
( React v.16.8 ↑) - React Redux Hooks –
useSelector()
,useDispatch()
( React Redux v7.1.0↑) - React Native Paper
以下の環境で構成しています。
- macOS Catalina 10.15.3
- expo 36.0.0
- @reduxjs/toolkit 1.2.5
- react-redux 7.2.0
- redux 4.0.5
- axios 0.19.2
- react-native-paper 3.6.0
この記事で分かること
- Redux を実装する一般的な手順
- Redux を実装する方法
- 一般的 Redux 実装方法及び使い方から、Redux Toolkit で Redux の実装を簡略化する方法 & 移行方法
- 関数コンポーネント ( フック )、クラスコンポーネント ( HOC ) への設定の違い
useReducer
useSelector
useDispatch
createSlice
などの簡単な使い方- Redux Toolkit 上での非同期処理方法、例 ( Counter、EmployeeList 閲覧 )
Redux の実装方法のまとめ
RTK を使用することで Constants 作成
段階を省略、Actions と Redux Slice 作成
段階を まとめて Redux Slice
で作成出来、一つに統合することが出来ます。
複雑な設定方法がシンプルになり、関数コンポーネントで useReducer
、useSelecter
、useDispatch
などのフックを使用するとコードは更にシンプルに作成できると思います。
React Native に Redux を実装する方法 | Basic | Redux Toolkit ( RTK ) | RTK + React Hook | RTK + Redux Hooks | RTK + Redux Hooks Thunk w/ Counter | RTK + Redux Hooks Thunk w/ EmployeeList |
初期セットアップ | ● | ← | ← | ← | ← | ← |
Constants 作成 | ● | X | X | X | X | X |
Actions 作成 | ● | X | X | X | X | X |
Reducer function 作成 | ● | X | X | X | X | X |
Redux Slice 作成 | X | ● | ● | ● | ● | ● |
Redux Store 作成 | ● | ● | ● | ● | ● | ● |
Redux Store を React Native アプリ ( Root ) へパスする | ● | ● | ● | ● | ● | ● |
React Native アプリを Redux Store へコネクトする | ● | ● | ● | ● | ● | ● |
▲ 縦軸:Redux を実装する一般的な手順、横軸:Redux を実装する方法 ( 個人的見解 )
これから ▲ の表をもとに +
ー
ボタンを押してカウントを変える「カウンター」を作成します。Expo 環境は整っていると想定します。
方法 #1:Basic
- Step 1初期セットアップ
Expo プロジェクトを作成
expo init redux-toolkit-practice
を実行し Expo プロジェクトを作成します。try🐶everything ~$ expo init redux-toolkit-practice ? Choose a template: expo-template-blank Using Yarn to install packages. You can pass --npm to use npm instead. Extracting project files... ..
Redux パッケージをインストールします。
別のターミナルを開いてから
npm install ...
を実行します。( macOS環境 )try🐶everything redux-toolkit-practice$ yarn add react-redux try🐶everything redux-toolkit-practice$ yarn add redux // react-native-paper は必須ではありません。 try🐶everything redux-toolkit-practice$ yarn add react-native-paper
※
react-redux
はredux
をコンポーネント上で、より簡単に使用するためのライブラリで、コンポーネントのStore
をprops
で受け取ったり、Subscribe
を自分で行う必要がありません。インストールが完了したら、
expo start
で Expo サーバを起動 & iOS シミュレータを起動しておきます。( Android エミュレータでも OK )try🐶everything ~$ cd redux-toolkit-practice/ try🐶everything redux-toolkit-practice$ expo start ... Couldn't start project. Please fix the errors and restart the project. ↑ようなエラーが発生すると、↓を実行してください。 try🐶everything redux-toolkit-practice$ npm install
iOS シミュレータ画面に
Open up App.js to start working on your app!
と表示されたことを確認します。必要なフォルダーを作成します。
try🐶everything redux-toolkit-practice$ mkdir src try🐶everything redux-toolkit-practice$ mkdir src/actions try🐶everything redux-toolkit-practice$ mkdir src/constants try🐶everything redux-toolkit-practice$ mkdir src/reducers try🐶everything redux-toolkit-practice$ mkdir src/components try🐶everything redux-toolkit-practice$ mkdir src/store
- Step 2Constants 作成
この時点でアプリの UI は確定して置きます。
(+
ー
ボタンを押して数字の変化を確認する「カウンターアプリ」を作成 )src/constants/index.js
export const COUNTER_CHANGE = "COUNTER_CHANGE";
- Step 3Actions 作成
Action オブジェクトを返す関数 ( Action Creators ) を作成します。
src/actions/counts.js
import { COUNTER_CHANGE } from "../constants"; export function changeCount(count) { return { type: COUNTER_CHANGE, payload: count }; }
- Step 4Reducer 関数作成
Reducer 関数は現在の
state
とaction
を引数として取り「新しい State」を返します。Action タイプがCOUNTER_CHNAGE
の場合、count
にaciton.payload
をセットして返します。src/reducers/countReducer.js
import { COUNTER_CHANGE } from "../constants"; const initialState = { count: 0 }; const countReducer = (state = initialState, action) => { switch (action.type) { case COUNTER_CHANGE: return { ...state, count: action.payload }; default: return state; } }; export default countReducer;
- Step 5Redux Store 作成
src/store/index.js
import { createStore, combineReducers } from "redux"; import countReducer from "../reducers/countReducer"; const rootReducer = combineReducers({ count: countReducer }); const configureStore = () => { return createStore(rootReducer); }; export default configureStore;
- Step 6React Native アプリを Redux Store へコネクトする
実装:クラスコンポーネント
mapStateToProps
mapDispatchToProps
bindActionCreators
connect
connect()
関数を使用してCounterBasic
クラスコンポーネントをRedux Store へ繋ぎます。ここで、Action を実行する関数 (
decrementCount()
、incrementCount()
) を作成し、ボタンをタップするたびにState
直に反映させます。- React Native アプリ = CounterBasic
src/components/CounterBasic.js
import React, { Component } from "react"; import { View, Button, Text } from "react-native"; import { connect } from "react-redux"; import { bindActionCreators } from "redux"; import * as countActions from "../actions/counts"; class CounterBasic extends Component { decrementCount() { let { count, actions } = this.props; count--; actions.changeCount(count); } incrementCount() { let { count, actions } = this.props; count++; actions.changeCount(count); } render() { const { count } = this.props; return ( <View> <Button title="+ CounterBasic" onPress={() => this.incrementCount()} /> <Text style={{ textAlign: "center" }}>Count: {count}</Text> <Button title="- CounterBasic" onPress={() => this.decrementCount()} /> </View> ); } } const mapStateToProps = state => ({ count: state.count.count }); const ActionCreators = Object.assign({}, countActions); const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(ActionCreators, dispatch) }); export default connect(mapStateToProps, mapDispatchToProps)(CounterBasic);
- Step 7Redux Store を React Native アプリ ( Root ) へパスする
実装:
configureStore
<Provider store={sore}> ... </Provider>
先ほどの
CounterBasic
クラスコンポーネントを表示させながら、react-redux
のProvider
を使用して Root アプリを Redux Store へ繋ぎます。App.js
import { SafeAreaView, View, Text } from "react-native"; import React from "react"; import { Provider } from "react-redux"; import { Divider, Colors } from "react-native-paper"; import CounterBasic from "./src/components/CounterBasic"; import configureStore from "./src/store"; const store = configureStore(); const MyDivider = () => <Divider style={{ backgroundColor: Colors.red900 }} />; export default function() { return ( <SafeAreaView style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <Provider store={store}> <View> <MyDivider /> <CounterBasic /> <MyDivider /> </View> </Provider> </SafeAreaView> ); }
- Step 8App Test
方法 #2:Redux Toolkit ( RTK )
Redux Toolkit ( 旧 redux-starter-kit ) を導入して「方法 #1」から Constants作成
を行わず、+
Actions と Reducer 関数
を一つにまとめることで一層シンプルさがアップします。
- Step 1初期セットアップ
「方法 #1」
+
以下の@redux-toolkit
をインストールします。try🐶everything redux-toolkit-practice$ yarn add @reduxjs/toolkit info Direct dependencies └─ @reduxjs/toolkit@1.2.5 info All dependencies ├─ @reduxjs/toolkit@1.2.5 ├─ immer@4.0.2 ├─ json-stringify-safe@5.0.1 ├─ redux-devtools-extension@2.13.8 ├─ redux-immutable-state-invariant@2.1.0 ├─ redux-thunk@2.3.0 └─ reselect@4.0.0 ✨ Done in 8.99s.
同時に
immer
json-stringify-safe
edux-devtools-extension
redux-immutable-state-invariant
redux-thunk
reselect
パッケージもインストールされます。src/modules
フォルダーを作成しておきます。try🐶everything redux-toolkit-practice$ mkdir src/modules
- Step 2Actions & Reducer 関数作成
Constants 作成
は不要です!Reducer 関数
を作成する際、Switch 文
は不要です。
RTK の createSlice を使用します。
src/modules/countsSlice.js
import { createSlice } from "@reduxjs/toolkit"; const countsSlice = createSlice({ name: "counter", initialState: 0, reducers: { increment: state => state + 1, decrement: state => state - 1 } }); export const { increment, decrement } = countsSlice.actions; export default countsSlice.reducer;
- Step 3Redux Store 作成
RTK の configureStore を利用します。
src/store/index.js
「方法 #1」
+
以下のように Slice 設定を追加します。import { combineReducers } from "redux"; import { configureStore } from "@reduxjs/toolkit"; import countReducer from "../reducers/countReducer"; import countsReducer from "../modules/countsSlice"; const rootReducer = combineReducers({ count: countReducer, counts: countsReducer }); const store = configureStore({ reducer: rootReducer }); export default store;
- Step 4React Native アプリを Redux Store へコネクトする
実装:クラスコンポーネント
createSlice
mapStateToProps
mapDispatchToProps
connect()
src/modules/CounterRTKwithSliceConnect.js
import React, { Component } from "react"; import { View, Button, Text } from "react-native"; import { connect } from "react-redux"; import { increment, decrement } from "../modules/countsSlice"; class CounterRTKwithSliceConnect extends Component { render() { const { counts } = this.props; console.log("!@# this.props: ", this.props); return ( <View> <Button title="+ RTKwithSliceConnect" onPress={() => this.props.increment()} /> <Text style={{ textAlign: "center" }}>Count: {counts}</Text> <Button title="- RTKwithSliceConnect" onPress={() => this.props.decrement()} /> </View> ); } } const mapStateToProps = state => ({ counts: state.counts }); const mapDispatchToProps = { increment, decrement }; export default connect( mapStateToProps, mapDispatchToProps )(CounterRTKwithSliceConnect);
- Step 5Redux Store を React Native アプリ ( Root ) へパスする
実装:
configureStore
store
<Provider store={sore}> ... </Provider>
「方法 #1」の
Step 7
でStore
にパス済!CounterRTKwithSliceConnect
コンポーネントを表示させます。App.js
import { SafeAreaView, View, Text } from "react-native"; import React from "react"; import { Provider } from "react-redux"; import { Divider, Colors } from "react-native-paper"; import CounterBasic from "./src/components/CounterBasic"; import CounterRTKwithSliceConnect from "./src/modules/CounterRTKwithSliceConnect"; import store from "./src/store"; const MyDivider = () => <Divider style={{ backgroundColor: Colors.red900 }} />; export default function() { return ( <SafeAreaView style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <Provider store={store}> <View> <MyDivider /> <CounterBasic /> <MyDivider /> <CounterRTKwithSliceConnect /> <MyDivider /> </View> </Provider> </SafeAreaView> ); }
- Step 6App Test
方法 #3:RTK + React Hook
useReducer()
は Redux Store へ登録されている特定の State 直を取得し dispatch
する方法を提供してくれます。
実装するには「方法 #2」の Step 4
と Step 5
だけを以下のように変更します。
- Step 1React Native アプリを Redux Store へコネクトする
実装:関数コンポーネント
createSlice
useReducer
src/modules/CounterRTKwithSliceReactHook.js
import React, { useReducer } from "react"; import { Text, View, Button } from "react-native"; import reducer, { increment, decrement } from "../modules/countsSlice"; const initialState = 0; export default function CounterRTKwithSliceReactHook() { const [counts, dispatch] = useReducer(reducer, initialState); return ( <View> <Button title="+ RTKwithSliceReactHook" onPress={() => dispatch(increment)} /> <Text style={{ textAlign: "center" }}>Count: {counts}</Text> <Button title="- RTKwithSliceReactHook" onPress={() => dispatch(decrement)} /> </View> ); }
- Step 2Redux Store を React Native アプリ ( Root ) へパスする
実装:
configureStore
store
<Provider store={sore}> ... </Provider>
「方法 #1」の
Step 7
でStore
にパス済!CounterRTKwithSliceReactHook
コンポーネントを表示させます。App.js
import { SafeAreaView, View, Text } from "react-native"; import React from "react"; import { Provider } from "react-redux"; import { Divider, Colors } from "react-native-paper"; import CounterBasic from "./src/components/CounterBasic"; import CounterRTKwithSliceConnect from "./src/modules/CounterRTKwithSliceConnect"; import CounterRTKwithSliceReactHook from "./src/modules/CounterRTKwithSliceReactHook"; import store from "./src/store"; const MyDivider = () => <Divider style={{ backgroundColor: Colors.red900 }} />; export default function() { return ( <SafeAreaView style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <Provider store={store}> <View> <MyDivider /> <CounterBasic /> <MyDivider /> <CounterRTKwithSliceConnect /> <MyDivider /> <CounterRTKwithSliceReactHook /> <MyDivider /> </View> </Provider> </SafeAreaView> ); }
- Step 3App Test
useReducer
は Redux Store を利用しないためredux-logger
にはロギングされません。
方法 #4:RTK + Redux Hooks
今回は React フック ( useReducer
) ではなく react-redux
フックの useSelector
useDispatch
を利用します。
実装するには「方法 #2」の Step 4
と Step 5
だけを以下のように変更します。
- Step 1React Native アプリを Redux Store へコネクトする
実装:関数コンポーネント
useSelector
useDispatch
createSlice
src/modules/CounterRTKwithSliceReduxHooks.js
import React from "react"; import { View, Button, Text } from "react-native"; import { increment, decrement } from "../modules/countsSlice"; import { useSelector, useDispatch } from "react-redux"; export default function CounterRTKwithSliceReduxHooks() { const counts = useSelector(state => state.counts); const dispatch = useDispatch(); return ( <View> <Button title="+ RTKwithSliceReduxHooks" onPress={() => dispatch(increment())} /> <Text style={{ textAlign: "center" }}>Count: {counts}</Text> <Button title="- RTKwithSliceReduxHooks" onPress={() => dispatch(decrement())} /> </View> ); }
- Step 2Redux Store を React Native アプリ ( Root ) へパスする
実装:
configureStore
store
<Provider store={sore}> ... </Provider>
「方法 #1」の
Step 7
でStore
にパス済!CounterRTKwithSliceReduxHooks
コンポーネントを表示させます。App.js
import { SafeAreaView, View, Text } from "react-native"; import React from "react"; import { Provider } from "react-redux"; import { Divider, Colors } from "react-native-paper"; import CounterBasic from "./src/components/CounterBasic"; import CounterRTKwithSliceConnect from "./src/modules/CounterRTKwithSliceConnect"; import CounterRTKwithSliceReactHook from "./src/modules/CounterRTKwithSliceReactHook"; import CounterRTKwithSliceReduxHooks from "./src/modules/CounterRTKwithSliceReduxHooks"; import store from "./src/store"; const MyDivider = () => <Divider style={{ backgroundColor: Colors.red900 }} />; export default function() { return ( <SafeAreaView style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <Provider store={store}> <View> <MyDivider /> <CounterBasic /> <MyDivider /> <CounterRTKwithSliceConnect /> <Text style={{ textAlign: "center", fontSize: 20 }}>Or</Text> <CounterRTKwithSliceReduxHooks /> <MyDivider /> <CounterRTKwithSliceReactHook /> <MyDivider /> </View> </Provider> </SafeAreaView> ); }
- Step 3App Test
方法 #5:RTK + Redux Hooks + Thunk w/ Counter
Redux Thunk
のような Middleware
の必要性は Redux 固有の動作方法から生まれます。
Action オブジェクト
は Store の唯一の情報源です。Action Creator
はAction オブジェクト
だけを返します。Reducer
はAction オブジェクト
を同期処理として Store に Dispatch します。
この場合、特定のアクションを数秒後に実行させたり、ネットワークリクエストなどのSide Effects には、普通の Action Creators
ではできません。
これらを可能にするには、Dispatch される Action と Reducer に到達する Action の間に Middleware
を配置することです。
Redux Thunk は、Action オブジェクト
の代わりに関数を返す Action Creators
を呼び出すことで特定のアクションを遅延させたり、ネットワークリクエストを Store に Dispatch することができます。( パラメーターとして Store メソッドの dispatch
と getState
を受け取ります。)
それでは、RTK にデフォルトで含まれている redux-thunk
を利用して非同期処理を行います。
- Step 1Redux Store に Middlewareとして thunkを追加する
既存のファイルに
import thunk from "redux-thunk";
middleware: [thunk]
を追加します。src/store/index.js
import { combineReducers } from "redux"; import { configureStore } from "@reduxjs/toolkit"; import thunk from "redux-thunk"; import countReducer from "../reducers/countReducer"; import countsReducer from "../modules/countsSlice"; const rootReducer = combineReducers({ count: countReducer, counts: countsReducer }); const store = configureStore({ reducer: rootReducer, middleware: [thunk] }); export default store;
- Step 2非同期処理をする関数コンポーネントを作成する
非同期
Dispatch
を実行する関数を返すAction Creator
を作成 ( 書き方の詳細 ) して React Native アプリを Redux Store へコネクトします。src/modules/RTKwithSliceReduxHooksThunkAsync.js
import React from "react"; import { View, Button, Text } from "react-native"; import { increment, decrement } from "../modules/countsSlice"; import { useSelector, useDispatch } from "react-redux"; const incrementAsync = () => dispatch => { setTimeout(() => { dispatch(increment()); }, 1000); }; const decrementAsync = () => dispatch => { setTimeout(() => { dispatch(decrement()); }, 1000); }; export default function RTKwithSliceReduxHooksThunkAsync() { const counts = useSelector(state => state.counts); const dispatch = useDispatch(); return ( <View> <Button title="+ RTKwithSliceReduxHooksThunkAsync" onPress={() => dispatch(incrementAsync())} /> <Text style={{ textAlign: "center" }}>Count: {counts}</Text> <Button title="- RTKwithSliceReduxHooksThunkAsync" onPress={() => dispatch(decrementAsync())} /> </View> ); }
- Step 3Redux Store を React Native アプリ ( Root ) へパスする
実装:
configureStore
store
<Provider store={sore}> ... </Provider>
「方法 #1」の
Step 7
でStore
にパス済!RTKwithSliceReduxHooksThunkAsync
コンポーネントを表示させます。App.js
import { SafeAreaView, View, Text, StyleSheet } from "react-native"; import React from "react"; import { Provider } from "react-redux"; import { Divider, Colors } from "react-native-paper"; import CounterBasic from "./src/components/CounterBasic"; import CounterRTKwithSliceConnect from "./src/modules/CounterRTKwithSliceConnect"; import CounterRTKwithSliceReactHook from "./src/modules/CounterRTKwithSliceReactHook"; import CounterRTKwithSliceReduxHooks from "./src/modules/CounterRTKwithSliceReduxHooks"; import RTKwithSliceReduxHooksThunkAsync from "./src/modules/RTKwithSliceReduxHooksThunkAsync"; import store from "./src/store"; const MyDivider = () => <Divider style={{ backgroundColor: Colors.red900 }} />; export default function() { return ( <SafeAreaView style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <Provider store={store}> <View> <MyDivider /> <CounterBasic /> <MyDivider /> <CounterRTKwithSliceReactHook /> <MyDivider /> <CounterRTKwithSliceConnect /> <Text style={styles.text}>↑↓</Text> <CounterRTKwithSliceReduxHooks /> <Text style={styles.text}> if ↓ click, it will be applied after 1 sec. </Text> <RTKwithSliceReduxHooksThunkAsync /> <MyDivider /> </View> </Provider> </SafeAreaView> ); } const styles = StyleSheet.create({ text: { textAlign: "center", fontSize: 20, marginVertical: 10, color: Colors.pink100 } });
- Step 4App Test
方法 #6:RTK + Redux Hooks + Thunk w/ EmployeeList
※「方法 #6」は この記事 からインサイトを得て RTK 用にコードを変更しています。
また、「方法 #5」の応用方法として、特定のサイトに ネットワークリクエストして従業員の情報を表示してみます。「方法 #5」のStep 1「Redux Store に Middlewareとして thunkを追加する」を設定した状態で進めます。
- Step 1Slice Reducer 関数を作成する
src/modules/pageListsSlice.js
import { createSlice } from "@reduxjs/toolkit"; const pageListsSlice = createSlice({ name: "PageLists", initialState: { pageList: [] }, reducers: { getPageList: (state, action) => { action.payload.data.map(list => { state.pageList.push(list); }); } } }); export const { getPageList } = pageListsSlice.actions; export default pageListsSlice.reducer;
- Step 2Redux Store 作成する
作成した
pageReducer
を Store に追加します。src/store/index.js
import { combineReducers } from "redux"; import { configureStore } from "@reduxjs/toolkit"; import thunk from "redux-thunk"; import countReducer from "../reducers/countReducer"; import countsReducer from "../modules/countsSlice"; import pageReducer from "../modules/pageListsSlice"; const rootReducer = combineReducers({ count: countReducer, counts: countsReducer, pageList: pageReducer }); const store = configureStore({ reducer: rootReducer, middleware: [thunk] }); export default store;
- Step 3非同期で従業員リストを取得するActionを作成する
axios
を利用します。yarn add axios
を実行してインストールしたあと、下記のファイルを作成します。※
axios
やfetch
で 普通に API データを取得するには、
例えば、App.js
にuseEffect
を利用する関数コンポーネントを作成してデータを取得したりします。import React, { useEffect } from "react"; import { View, Text } from "react-native"; import axios from "axios"; const URLs = { employees: "http://dummy.restapiexample.com/api/v1/employees", fakePosts: "https://jsonplaceholder.typicode.com/posts/1", fakeTodos: "https://jsonplaceholder.typicode.com/todos/2" }; export const App = props => { useEffect(() => { axios.get(URLs.employees).then(response => console.log(response.data)); fetch(URLs.fakeTodos) .then(response => response.json()) .then(json => console.log(json)); }, []); return ( <View> <Text>App</Text> </View> ); };
コンソールで確認すると ▼ のようになります。
// employees Object { "data": Array [ Object { "employee_age": "61", "employee_name": "Tiger Nixon", "employee_salary": "320800", "id": "1", "profile_image": "", }, Object { "employee_age": "63", "employee_name": "Garrett Winters", "employee_salary": "170750", "id": "2", "profile_image": "", }, Object { "employee_age": "66", ... // fakeTOdos Object { "completed": false, "id": 2, "title": "quis ut nam facilis et officia qui", "userId": 1, }
一方、
Thunk
を利用する際は少し書き方が変わります。
非同期Dispatch
を実行する関数を返すAction Creator
を作成 ( 書き方の詳細 ) します。src/actions/getPageListAsync.js
import axios from "axios"; import { getPageList } from "../modules/pageListsSlice"; export const getPageListAsync = () => dispatch => { try { axios.get("http://dummy.restapiexample.com/api/v1/employees").then(res => { dispatch(getPageList(res.data)); }); } catch (err) { console.error(err); } };
- Step 4React Native アプリを Redux Store へコネクトする
実装:関数コンポーネント
useSelector
useDispatch
createSlice
Redux Thunk
src/modules/PageListWithThunkAsync.js
import React from "react"; import { View, Button, Text } from "react-native"; import { useSelector, useDispatch } from "react-redux"; import { getPageListAsync } from "../actions/getPageListAsync"; export default function PageListWithThunkAsync() { const pageLists = useSelector(state => state.pageList.pageList); const dispatch = useDispatch(); return ( <View> <Button disabled={pageLists.length ? true : false} title="Get Employee" onPress={() => dispatch(getPageListAsync())} /> {pageLists.length ? ( pageLists.map((employee, i) => ( <Text key={i}> {employee.employee_name} / {employee.employee_age} </Text> )) ) : ( <Text style={{ textAlign: "center" }}>Click ↑↑↑</Text> )} </View> ); }
- Step 5Redux Store を React Native アプリ ( Root ) へパスする
実装:
configureStore
store
<Provider store={sore}> ... </Provider>
「方法 #1」の
Step 7
でStore
にパス済!PageListWithThunkAsync
コンポーネントを表示させます。App.js
import { SafeAreaView, ScrollView, Text, StyleSheet } from "react-native"; import React from "react"; import { Provider } from "react-redux"; import { Divider, Colors } from "react-native-paper"; import CounterBasic from "./src/components/CounterBasic"; import CounterRTKwithSliceConnect from "./src/modules/CounterRTKwithSliceConnect"; import CounterRTKwithSliceReactHook from "./src/modules/CounterRTKwithSliceReactHook"; import CounterRTKwithSliceReduxHooks from "./src/modules/CounterRTKwithSliceReduxHooks"; import RTKwithSliceReduxHooksThunkAsync from "./src/modules/RTKwithSliceReduxHooksThunkAsync"; import PageListWithThunkAsync from "./src/modules/PageListWithThunkAsync"; import store from "./src/store"; const MyDivider = () => <Divider style={{ backgroundColor: Colors.red900 }} />; export default function() { return ( <SafeAreaView style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <Provider store={store}> <ScrollView> <MyDivider /> <CounterBasic /> <MyDivider /> <CounterRTKwithSliceReactHook /> <MyDivider /> <CounterRTKwithSliceConnect /> <Text style={styles.text}>↑↓</Text> <CounterRTKwithSliceReduxHooks /> <Text style={styles.text}> if ↓ click, it will be applied after 1 sec. </Text> <RTKwithSliceReduxHooksThunkAsync /> <MyDivider /> <PageListWithThunkAsync /> <MyDivider /> </ScrollView> </Provider> </SafeAreaView> ); } const styles = StyleSheet.create({ text: { textAlign: "center", fontSize: 20, marginVertical: 10, color: Colors.pink100 } });
- Setp 6App Test
まとめ
※ この記事は このブログ からインサイトを得たものです。
Redux を使うなら、Redux Toolkit は必須で、+ React Hook ( useReducer ) + Thunk の使い方が良いかと思いました。
- 小規模( small ) のアプリ:useState
- 中規模 ( medium ) のアプリ:useState + useReducer + useContext
- 大規模 ( large ) のアプリ:useState + useReducer + Redux
▲ State 管理方法からするとこの記事は 3 番目の複雑で大規模のアプリに実装するケースに当たります。その際に RTK で Redux を実装するのがより楽になると思います。( 全体の Github Repo は ここ から)
※ 以下はこの記事の実践版として、ご参考ください。( 2020/03/06 追記 )
material-bottom-tabsのTabを非表示する方法〜React navigation〜
※ React Context API
で State
を管理する方法:( 2020/03/16 追記 )
Reduxの代わりにReact Context APIでStateを管理してみる
コメント