ミドルウェアを使わないreduxをちゃんと理解するための個人メモ
仕事でいきなりredux/redux-saga
さらに redux-actions
の環境を触ってきて
ミドルウェアを使わないシンプルな構成のreduxの理解が浅かったのでちゃんと覚えようと思った
環境構築
お手軽 create-react-app
$ create-react-app react-redux-example
次にreduxを使うのに必要なnpmモジュールをインストールする
$ yarn add redux react-redux
react-redux
は react
で redux
を扱うために必要
これだけで環境は整った
処理の流れ
ActionType
ただの変数
イベントの名前を付けてく
const ADD_ITEM = 'ADD_ITEM' const DELETE_ITEM = 'DELETE_ITEM'
Action
オブジェクトを返すだけのピュアな関数
typeを設定するのはお約束
// typeにActionTypeを設定 export const addItem = item => ({ type: ADD_ITEM, item }) export const deleteItem = id => ({ type: DELETE_ITEM, id }) // addItem('hello') // -> { type: 'ADD_ITEM', item: 'hello' }
Reducer
storeの構造はここで決まる
import { combineReducers } from 'redux' // 初期値 const initialState = [ { item: 'hello' } ] const todos = (state = initialState, action) => { switch (action.type) { // アイテムを追加 case ADD_ITEM: return [...state, { item: action.item }] // アイテムを削除 case DELETE_ITEM: return state.filter((v, k) => k !== action.id) default: return state } } // 分割されたreducerを合成 // これがstore全体の構造になる const reducer = combineReducers({ todos })
Storeを生成
import { createStore } from 'redux' // 前述のReducersで生成した値が入る const store = createStore(reducer)
ミドルウェアがある場合はcreateStoreの第2引数に設定を渡すが
今回は素のredux構成なので省略
描画する
index.js
を編集
import { Provider } from 'react-redux' // ... ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) // ...
ここでやっとreact-redux
が登場
storeの値やActionが使えるようになる
App.js
container
となるものは全てこの形式で書かれる
import { connect } from 'react-redux' class App extends Component<{}> { render() { const { todos, actions } = this.props return ( <div> <button onClick={() => actions.addItem('world')}>ADD</button> <ul> {todos.map((v, k) => <li onClick={() => actions.deleteItem(k)}>{v.item}</li>)} </ul> </div> ) } } // stateにstoreの値が入ってる // this.props.todosで使えるようになる const mapStateToProps = state => ({ todos: state.todos }) // Actionsで定義した関数が使えるようになる // this.props.actions.addItem('hello') const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Actions, dispatch) }) // Appコンポーネントに各値を接続 export default connect(mapStateToProps, mapDispatchToProps)(App)
まとめ
処理の流れが分かってスッキリした
今回のソース
わかりやすいように出来るだけ1ファイルに納めて書いてる
次はミドルウェア
を導入してどう変わっていくかを確かめる
※追記
タイトル変えました
以上
igniteでモダンなReactNative環境を楽々セットアップ
react-native init app
でアプリを始めても最初はredux
やmiddleware
など
いつも決まって使うような環境は入っていない
とりあえず試したいけど面倒くさいよ!って時はignite
コマンドを使うと
定番の環境、いわゆるボイラープレートをいい感じに構築してくれる
そうでない人も、個人的にフォルダ構成や書き方は参考になった
準備
react-native
まずはReactNativeを動かせる環境を作る
Getting Started - React Native
$ brew install node $ brew install watchman $ npm install -g react-native-cli
ignite
ignite
をインストール
$ npm install -g ignite-cli
ignite
コマンドを使うと
対話形式で定番のライブラリを導入するか聞かれていき
最後に全て設定が整ったプロジェクトが生成される
この生成されるボイラープレートには種類があり
下のリンクで配布されている中から -b
で指定出来る
ignite/BOILERPLATES.md at master · infinitered/ignite · GitHub
また、自分でボイラープレートを作成することも出来る
ignite/creating-boilerplates.md at master · infinitered/ignite · GitHub
$ ignite new MyNewAppName -b ir-boilerplate
何もなも指定しないと、デフォルトで redux/redux-saga
がメインの構成になる
試す
$ ignite new igniteApp
実行した後、いくつか質問されます
- Would you like Ignite Development Screens?
- 動いてるサンプルが見たい場合は
yes
- 動いてるサンプルが見たい場合は
- What vector icon library will you use?
- 用意された様々なiconを表示出来るツールが使いたい場合は
yes
- 用意された様々なiconを表示出来るツールが使いたい場合は
- What internationalization library will you use?
- 多言語対応したい場合は
yes
- 多言語対応したい場合は
- What animation library will you use?
- 便利なアニメーション ライブラリが使いたい場合は
yes
- 便利なアニメーション ライブラリが使いたい場合は
とりあえず全部yes
にする
すると以下のようなフォルダが生成される
├── App │ ├── Components │ ├── Config │ ├── Containers │ ├── Fixtures │ ├── I18n │ ├── Images │ ├── Lib │ ├── Navigation │ ├── Redux │ ├── Sagas │ ├── Services │ ├── Themes │ └── Transforms │ ├── README.md ├── Tests ├── android ├── app.json ├── ignite ├── index.js ├── ios ├── node_modules ├── package.json ├── storybook └── yarn.lock
storybookもデフォルトで設定されてて嬉しい
起動
$ react-native run-ios
コンポーネント・API・テーマ・画面遷移など、サンプルコードが一式確認出来るので
とりあえずコードを眺めたりいじったりしてみると良いのではないでしょうか
時間があったら自分用のボイラープレート作ってみる予定
以上
ReactNativeのAsyncStorageをコンソールから直接操作する
前回の記事で導入したReact Native Debugger
を使うと
AsyncStorage
を直接操作することが出来る
AsyncStorageとは
アプリ本体にデータを保存したい時に使えるkey-valueストア
永続化されるのでアプリを落としてもデータが保持される
実際にはSQLiteをラップしてるっぽい
AsyncStorage
をラップしてプロミスを返すようにしたこのモジュールがおすすめ
ちょっと書きやすくなってる
直接操作する
一度データを保存すると、後で消したりデバッグするのが面倒くさい
そこでReact Native Debugger
を使うとコンソールから直接操作することが出来る
const store = require('AsyncStorage') // 指定したキーを削除する store.removeItem('key') // 値をセットする store.setItem('key', 'hello!')
とくに特別なことしてるわけではなく、普通にAsyncStorage
をインポートして直接操作してるだけ
他の細かい操作はドキュメントを参照
本当はGUIで操作したいけど、そんな方法があれば知りたい
以上
React Native Debuggerを使ってみる
React Nativeで開発をする際 redux
を使用することが多いと思う
そんな時に使うと開発が捗るのが React Native Debugger
インストール
$ brew update && brew cask install react-native-debugger
インストールが完了すると、アプリ一覧にアイコンが現れる
通常RNのアプリ上でリモートデバックをONにするとブラウザのデバッグコンソールが開くが
React Native Debuggerを立ち上げておくと、そっちにつながるようになる
ブラウザのデバッカーが立ち上がってる場合は先に閉じる
アプリをリロードしてちゃんとつながればOK
既にこれだけでもデバックコンソールとしての役割を果す
reduxのデバッグを出来るようにする
ベースとして適当なサンプルアプリを落としてそれに組み込んでみる
$ git clone https://github.com/react-native-training/react-native-redux-saga-example $ yarn $ react-native run-ios
起動画面
redux + redux-saga構成
ボタンを押すとAPIを叩いてデータを表示するだけのすっごいシンプルなデモアプリ
ここからが組み込み方
reduxのstoreの状態やactionを見るために
redux-devtools-extension
というパッケージが必要になる
$ yarn add redux-devtools-extension --dev
ストアの初期化をしてる箇所のコードのミドルウェアのところにデバッガーを仕込むだけ
const store = createStore(
app,
applyMiddleware(sagaMiddleware)
)
↓↓
import { composeWithDevTools } from 'redux-devtools-extension' const store = createStore( app, composeWithDevTools(applyMiddleware(sagaMiddleware)) )
差分
react native debugger · rskull/react-native-redux-saga-example@1070ab4 · GitHub
デモ
これでイベントが起こる度にデバッガーで細かいやり取りを見れるようになった
発行されたaction、storeの差分やpayloadの中身が表示される
前の状態に戻ってリプレイなど、いろいろな事が出来るようになってすっごい便利
是非、導入してみてはいかかでしょうか
以上です