はじめに
Expo+Redux(+firebase)でログインフォーム① 〜概要・Expoの準備〜の続きです。
今回の記事では、Reduxを使って、まずは簡単なカウンターを作っていきます。
次々回の記事で、ログイン画面と画面遷移を実装していく予定です。
現在のディレクトリ構成
root/
├ App.js
├ package.json
srcディレクトリの作成
rootに色々と詰め込んでいくとわかりにくくなるので、srcディレクトリを作ってAppを移しましょう
Expoはデフォルトでroot/App.jsを読み込むようになっているので、これを変更するために、node_modules/expo/AppEntry.jsを編集します
-- import App from '../../App';
++ import App from '../../src/App';
そしてroot/App.jsを削除し、root/src/App.tsxを書いていきます。
Reduxを導入
まずはApp.tsxにreduxを入れていきます
とりあえず最小の構成で、1ファイルにまとめています
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createStore, combineReducers } from 'redux'
import { Provider, connect } from 'react-redux'
// reducer
function counter(state, action) {
if (action.type === 'undefined') {
return null
}
switch(action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return null
}
}
// store
const store = createStore(combineReducers({ count: counter }))
// component
function Counter({ count, dispatch }) {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{count}</Text>
<Button
title='Increment'
onPress={() => dispatch({ type: 'INCREMENT' })}
/>
<Button
title='DECREMENT'
onPress={() => dispatch({ type: 'DECREMENT' })}
/>
</View>
)
}
// container
const CounterContainer = connect(state => ({ count: state.count }))(Counter)
export default function App() {
return (
<Provider store={store}>
<CounterContainer />
</Provider>
)
}
// スタイル
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
})
Reducer
// reducer
function counter(state, action) {
if (action.type === 'undefined') {
return null
}
switch(action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return null
}
}
actionと現在のstateを受け取って、新しいstateを返す関数です。
actionがない時や、swich文のdefaultでnullを返すようにしないとエラーになるので、注意が必要です。
Store
// store
const store = createStore(combineReducers({ count: counter }))
Reducerが今後増えることを踏まえて、combineReducerでまとめておきます。
Component/Container
// component
function Counter({ count, dispatch }) {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{count}</Text>
<Button
title='Increment'
onPress={() => dispatch({ type: 'INCREMENT' })}
/>
<Button
title='DECREMENT'
onPress={() => dispatch({ type: 'DECREMENT' })}
/>
</View>
)
}
// container
const CounterContainer = connect(state => ({ count: state.count }))(Counter)
画面に表示する部分です。
ボタンを押すと、typeがINCREMENT/DECREMENTのactionがdispatchされます。
ボタンを押した時のコールバック関数は、本来はContainer側に書くべきだとは思いますが、コードが少しややこしくなるので、それは後にしようと思います。
App
export default function App() {
return (
<Provider store={store}>
<CounterContainer />
</Provider>
)
}
ReactNativeは、で囲ってstoreを渡してやれば、Reduxと接続することができます。
次回
今回は最低構成のreduxを組んでみたので、次回はDucksパターンを意識してファイルを分けていきたいと思います。
その後、ReactNativeDebuggerも使えるようにしていきます。
Expo+Redux(+firebase)でログインフォーム③ 〜ファイル整理・Debugger〜