素のreduxをちゃんと理解するための個人メモ

仕事でいきなりredux/redux-saga さらに redux-actions の環境を触ってきて

素のreduxの理解が浅かったのでちゃんと覚えようと思った次第

環境構築

お手軽 create-react-app

github.com

$ create-react-app react-redux-example

次にreduxを使うのに必要なnpmモジュールをインストールする

$ yarn add redux react-redux

react-reduxreactredux を扱うために必要

これだけで環境は整った

処理の流れ

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を渡すと

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)

f:id:rskull:20171122012742g:plain

スッキリした

次はミドルウェアを導入してどう変わっていくかを確かめる

以上

igniteでモダンなReactNative環境を楽々セットアップ

f:id:rskull:20171117015447p:plain

react-native init appでアプリを始めても最初はreduxmiddlewareなど

いつも決まって使うような環境は入っていない

とりあえず試したいけど面倒くさいよ!って時はigniteコマンドを使うと

定番の環境、いわゆるボイラープレートをいい感じに構築してくれる

そうでない人も、個人的にフォルダ構成や書き方は参考になった

github.com

準備

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
  • What internationalization library will you use?
    • 多言語対応したい場合はyes
  • What animation library will you use?
    • 便利なアニメーション ライブラリが使いたい場合はyes

とりあえず全部yesにする

f:id:rskull:20171117012631g:plain

すると以下のようなフォルダが生成される

├── 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

f:id:rskull:20171117014902g:plain

コンポーネントAPI・テーマ・画面遷移など、サンプルコードが一式確認出来るので

とりあえずコードを眺めたりいじったりしてみると良いのではないでしょうか

時間があったら自分用のボイラープレート作ってみる予定

以上

ReactNativeのAsyncStorageをコンソールから直接操作する

前回の記事で導入したReact Native Debuggerを使うと

AsyncStorageを直接操作することが出来る

rskull.hateblo.jp

AsyncStorageとは

アプリ本体にデータを保存したい時に使えるkey-valueストア

永続化されるのでアプリを落としてもデータが保持される

実際にはSQLiteをラップしてるっぽい

AsyncStorage - React Native

AsyncStorageをラップしてプロミスを返すようにしたこのモジュールがおすすめ

github.com

ちょっと書きやすくなってる

直接操作する

一度データを保存すると、後で消したりデバッグするのが面倒くさい

そこでReact Native Debuggerを使うとコンソールから直接操作することが出来る

const store = require('AsyncStorage')

// 指定したキーを削除する
store.removeItem('key')

// 値をセットする
store.setItem('key', 'hello!')

とくに特別なことしてるわけではなく、普通にAsyncStorageをインポートして直接操作してるだけ

他の細かい操作はドキュメントを参照

github.com

本当はGUIで操作したいけど、そんな方法があれば知りたい

以上

React Native Debuggerを使ってみる

React Nativeで開発をする際 redux を使用することが多いと思う

そんな時に使うと開発が捗るのが React Native Debugger

インストール

github.com

$ brew update && brew cask install react-native-debugger

インストールが完了すると、アプリ一覧にアイコンが現れる

f:id:rskull:20171022023039p:plain

通常RNのアプリ上でリモートデバックをONにするとブラウザのデバッグコンソールが開くが

React Native Debuggerを立ち上げておくと、そっちにつながるようになる

ブラウザのデバッカーが立ち上がってる場合は先に閉じる

アプリをリロードしてちゃんとつながればOK

既にこれだけでもデバックコンソールとしての役割を果す

reduxのデバッグを出来るようにする

ベースとして適当なサンプルアプリを落としてそれに組み込んでみる

github.com

$ git clone https://github.com/react-native-training/react-native-redux-saga-example
$ yarn
$ react-native run-ios

起動画面

f:id:rskull:20171023011754p:plain

redux + redux-saga構成

ボタンを押すとAPIを叩いてデータを表示するだけのすっごいシンプルなデモアプリ

ここからが組み込み方

reduxのstoreの状態やactionを見るために

redux-devtools-extension というパッケージが必要になる

github.com

$ 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

デモ

これでイベントが起こる度にデバッガーで細かいやり取りを見れるようになった

f:id:rskull:20171023014450g:plain

発行されたaction、storeの差分やpayloadの中身が表示される

前の状態に戻ってリプレイなど、いろいろな事が出来るようになってすっごい便利

f:id:rskull:20171023014222p:plain

是非、導入してみてはいかかでしょうか

以上です