Help us understand the problem. What is going on with this article?

世界一シンプルなReactNative + Reduxチュートリアル①

alt
世界一シンプルなReactNative + Reduxチュートリアル②

フロントエンジニア出身以外のreact-native初心者が戸惑うのはreduxの実装だ。reduxは学習コストが高いけど、アプリのパフォーマンスを上げてくれるので、実務では必須になる。
2018年のreactのstate管理はreduxとmobxが主流だ。
世の中の主流はreduxで、redux-persistなど便利なライブラリがたくさんある。
もしあなたが、スタートアップ/新規開発の人間ならreduxは止めておこう。reduxは学習コストが高い+プロトタイプ開発速度が遅いので辛い。
スタートアップには、global state管理を諦める。またはunstatedかcontext apiをおすすめしたい。unstateはreactのstate管理の決定版だ。
わずか400行のライブラリだが、globalState、非同期、といったものを全て解決できる。(react-navigationと使うには一癖あるが)
starもかなり多い。

そこで究極にシンプルなチュートリアルを作ってみた。

アプリを作成する。

expo init simpleTuto
cd simpleTuto

reduxをインストールする

npm install --save redux react-redux

元からあるApp.jsと以下の2ファイルだけで実装する。

touch home.js redux.js

まず、軽くreduxの概念をおさらいしていこう。

reduxに登場する大きな概念は4つ。
1. store
reactのstateを保管する場所。jsonで記述される。storeのstateを変更するには、setStateではなく、reducerメソッドを使う必要がある。
2. reducer
storeを書き換えるための関数。jsonを引数としてとり、storeのstateを書き換える。
3. action
reducerに渡すjsonのこと。
紛らわしいが、action creatorが情報をjsonに加工する関数。actionがjson
4. connect
storeに保管されたstateはreact-reduxのconnect関数を経由して、componentに渡される。

それでは書いていこう。今回はシンプルにするために、reduxの機能を1ファイルにまとめる。

// redux.js
import {  
  combineReducers,
  createStore,
} from 'redux';

// actions.js
// actionはreduxの機能でなく、オブジェクトを作るための純粋なjsの関数です。
// 下のcloseKabayaの3行をchromeを開き、command + option + iでコンソールを開き貼り付けましょう。
// その後、console.log(deleteName())で、{type: "DELETE_NAME", name: ''}というオブジェクトが生成されるのを確かめましょう。
export const deleteName = () => ({  
  type: 'DELETE_NAME',
  name: ''
});

// 引数nameをとり、{type: "ADD_NAME", name: name}を返すjsの関数。
export const setName = name => ({  
  type: 'ADD_NAME',
  name: name,
});

INITIAL_STATE = {
  name: 'Nanasi'
}

// reducers.js
// reduxではglobal stateを巨大なjson(store)として管理します。stateの変更はjsonの書き換えによってのみ管理します。
// actionは純粋なjsのオブジェクトを作る関数であることを思い出してください。
// reducerはactionで生成されたオブジェクトを受け取り、巨大なjson(store)を書き換える関数です。
const reducer = (state = INITIAL_STATE, action) => {  
  switch (action.type) {
    case 'ADD_NAME':
      return {...state, name: action.name}
    case 'DELETE_NAME':
      return {...state, name: ''}
    default:
      return state;
  }
}

export const reducers = combineReducers({  
  user: reducer
})

// store.js
export const store = createStore(reducers)

// storeは巨大なjsonです。storeの中身を取り出すにはgetStateメソッドを使います。
// エミュレータでcommand + dを押し、enable remote debugをクリックしましょう。
// debuggerが現れるので、consoleタブをクリックし、エミュレータ上でアプリをcommandd + rで再起動しましょう。
console.log(store.getState())

// arrayやobjectを綺麗に表示したい時はconsole.tableが便利です。
console.table(store.getState())

// storeはjsonです。つまりjsのオブジェクトです。 jsの関数のtypeofでstoreのstateがオブジェクトであることを確かめましょう。
console.log(typeof store.getState)

// storeもまたjsのオブジェクトであり、4つしかメソッドを持たないことを確認しておきましょう。
console.log(store)

次にホーム画面を作る。

理解が難しいAPIは以下。名が体を表していていいネーミングだ。

mapStateToProps = storeのstateをcontainerコンポーネントのpropsに変換する。

mapDispatchToProps = storeから関数を取得したい時、 stateをcontainerコンポーネントのpropsに変換する。

connect = reactで書かれたコンポーネントはそのままではreduxで使えないので、reduxのために新しいコンポーネントを作り出しreduxと結合する。
// home.js
import React, {Component} from 'react'
import { View, Text, Button } from 'react-native'
import { connect } from 'react-redux'
import { setName, deleteName } from './redux'
import {store} from './redux'

export class Home extends Component {
  render() {
    return (
      <View style={{flex: 1, justifyContent: 'space-around', alignItems: 'center'}}>
        <Text style={{marginTop: 100}}>My name is {this.props.name}.</Text>
        <View style={{flexDirection: 'row'}}>
          <Button
            onPress={this.props.deleteName}
            title="deleteName"
          /> 
          <Button
            onPress={() => this.props.setName('カバヤ')}
            title="setName"
          />
        </View>
        {/* ストアは、以下の様なJSONで記述されています。combine reducerのキーにuserを使ったのでuserプロパティの中に、stateが保存されます。 */}
        {/* stor のstateを取り出すにはgetStateメソッドを使います。JSON.stringifyで文字列へと変換しています。 */}
        <Text style={{marginBottom: 100}}>現在のstore: {JSON.stringify(store.getState())}</Text>
      </View>
    )
  }
}

const mapStateToProps = state => ({
  // storeは巨大なJsonの塊なので、nameにjsonから取って来たデータを代入している。 
  name: state.user.name
})

const mapDispatchToProps = {
  // importしたactionCreatorを記述。
  setName,
  deleteName
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Home)

最後に、作った全てのコンポーネントをProviderで囲む。

// index.js
import React, {Component} from 'react'
import { Provider } from 'react-redux'
import { store } from './redux'
import Home from './home'

export default class reduxApp extends Component {
  render() {
    return (
      <Provider store={store}>
        <Home />
      </Provider>
    )
  }
}

完成だ。
まず、初期のstoreが以下のようになる。
スクリーンショット 2018-11-26 20.26.10.png
その後、deleteNameを押すと、、、
スクリーンショット 2018-11-26 20.26.25.png
また、setNameを押すと、
スクリーンショット 2018-11-26 20.26.21.png
のようになる。

npm start
で確認してみよう。

えっ。redux記述量が多くて学習コストも高いって?
ご安心ください。ES6で記述量を減らせますよ!
爆速Redux ES6だけで現代風コードダイエット

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした