世界一シンプルなReact-native + Reduxチュートリアル２

![alt](https://user-images.githubusercontent.com/26253721/39765058-e8d0eae4-531b-11e8-9305-4c852f269517.png)

シンプルなTodoアプリのstate管理をreduxへと移します。
reduxではまりがちなArrayを使ったいいサンプルだと思います。
なぜならreduxではmutativeなメソッドであるpush, spliceなどの配列操作のメソッドは非推奨です。
ES6のスプレッド演算子かsliceメソッドを使いましょう。

それでは以下のtodoアプリをgit cloneしてください。
https://github.com/brunolsantos/React-Native-ToDoApp
react-native run-iosして動くことを確認しましょう。
以下のような画面が現れます。
![スクリーンショット 2018-04-17 午前0.01.45.png](https://qiita-image-store.s3.amazonaws.com/0/183835/4a1ee5ea-eb15-3bba-adb7-ac9acffbd377.png)

さあそれではこのシンプルなアプリにreduxを入れていきましょう。
まずはrootフォルダで

```
yarn add redux react-redux
```
reduxとreactを一緒に使うにはreact-reduxが必要です。

必要なフォルダ、ファイルを作りましょう。
以下のようにフォルダとファイルを作り直してください。
![スクリーンショット 2018-04-15 午後9.58.38.png](https://qiita-image-store.s3.amazonaws.com/0/183835/7997945b-895c-820b-1ef1-00c14f23db11.png)

### 1. rootReducerを作ります。
rootReducerはreduxのAPIのcombineReducersで全てのreducerを一つにまとめ扱いやすくしたreducerです。

```jsx

// src/rootReducer
import { combineReducers } from "redux";
import HomeReducer from "./Home/reducer";

export default combineReducers({
  home: HomeReducer
  // mapStateToPropsでstate.home.fooなどと参照できる
});
```

### 2. storeを作ります。
state専用データベースのようなものです。実態は巨大なjsonです。一度、console.log(store)して確かめてみましょう。ここにstateを集約することでスパゲッティを防いでいます。

```js
// src/store.js
import { createStore } from "redux";
import rootReducer from "./rootReducer";

export const store = createStore(rootReducer);
```

### 3. Providerで配下のコンポーネントにstoreを渡します。
JSXで囲んであげましょう。

```jsx

// src/App.js
import React, { Component } from "react";
import { Provider } from "react-redux";
import { store } from "./store";
import Home from "./Home/homeContainer";

const App = () => (
  <Provider store={store}>
    <Home />
  </Provider>
);

export default App;
```

以上で、どのアプリでも必要なreduxのセットアップを書き終えました。

次にactionを作っていきましょう。actionとはreducerで扱うデータをjsonにしたものです。reducerは純粋な関数、actionは関数に渡すオブジェクト/引数と考えてokです。なお、actionはreduxの機能ではなく、純粋なjsです。
const ADD_NOTE = "ADD_NOTE"の部分はredux的に必要です。
諦めて書きましょう。
### 4. actionを作成する。

```js
// src/Home/actions.js
export const ADD_NOTE = "ADD_NOTE";
export const DELETE_NOTE = "DELETE_NOTE";

export const addNote = payload => ({
  type: "ADD_NOTE",
  payload: { noteText: payload }
});

export const deleteNote = payload => ({
  type: "DELETE_NOTE",
  payload
});
```

### 5. reducerを作成する。
ここで作成したreducerをrootReducerに食わせます。reducerは純粋な関数です。
reducerはstateを加工してdispatchでstoreに保存します。
switch文でactionの名前ごとに処理を分けます。
reducer自体に名前は必要ないので省略していますが、（export defaultされるものに名前は必要ない）名前をつけることも出来ます。

```jsx

// src/Home/reducer.js
const INITIAL_STATE = {
  noteArray: []
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case "ADD_NOTE":
      return {
        noteArray: [...state.noteArray, action.payload]
      };
    case "DELETE_NOTE":
      return {
        ...state,
        noteArray: [...state.noteArray, action.payload]
      };
    default:
      return state;
  }
};
```

### 7. containerを作成する。containerとはreduxからstateを受け取るコンポーネントです。connect関数を利用してstoreからstateを受け取ります。stateを受け取らないコンポーネントはcomponentフォルダに置くのが一般的です。
簡潔に言うと、
mapStateToProps = グローバルstate
mapDispatchToProps = dispatchする関数
を表します。。

```jsx
// src/Home/homeContainer.js
import React, { Component } from "react";
import {
  View,
  Text,
  StyleSheet,
  TextInput,
  ScrollView,
  TouchableOpacity
} from "react-native";
import { connect } from "react-redux";
import { addNote, deleteNote } from "./actions";

import Note from "./components/note";

class Home extends Component {
  // constructorはthis.stateを使う時のみ必要。reactの機能ではなく、jsの機能のなので注意。
  constructor(props) {
    super(props);
    this.state = {
      noteText: ""
    };
  }

  // renderとreturnの間には、render以下で使う変数を定義できる。ここではJSXをnotesという変数に入れている。
  // arrayの中には、keyを保存せず、子コンポーネントに渡すときにゼロからmapでキーを生成している。
  render() {
    let notes = this.props.noteArray.map((val, key) => {
      return (
        <Note
          key={key}
          keyval={key}
          val={val}
          deleteMethod={() => this.deleteNote(key)}
        />
      );
    });
    return (
      <View style={styles.container}>
        <View style={styles.header}>
          <Text style={styles.headerText}>- NOTER -</Text>
        </View>
        <ScrollView style={styles.scrollContainer}>{notes}</ScrollView>

        <View style={styles.footer}>
          <TextInput
            style={styles.textInput}
            placeholder=">note"
            onChangeText={noteText => this.setState({ noteText })}
            value={this.state.noteText}
            placeholderTextColor="white"
            underlineColorAndroid="transparent"
          />
        </View>
        <TouchableOpacity
          onPress={this.addNote.bind(this)}
          style={styles.addButton}
        >
          <Text style={styles.addButtonText}>+</Text>
        </TouchableOpacity>
      </View>
    );
  }

  addNote() {
    if (this.state.noteText) {
      this.props.onAddNote(this.state.noteText);
      this.setState({ noteText: "" });
    }
  }

  deleteNote(key) {
    this.state.noteArray.splice(key, 1);
    this.setState({ noteArray: this.state.noteArray });
  }
}

const mapStateToProps = state => {
  // stateとはstoreのこと。巨大なjsonで全てのstateを管理する
  console.log(state)
  return {
    noteArray: state.home.noteArray
  };
};

const mapDispatchToProps = dispatch => {
  // dispatchも確認してみよう。
  console.log(dispatch)
  return {
    onAddNote: text => dispatch(addNote(text))
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Home);

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  header: {
    backgroundColor: "#E91E63",
    alignItems: "center",
    justifyContent: "center",
    borderBottomWidth: 10,
    borderBottomColor: "#ddd"
  },
  headerText: {
    color: "white",
    fontSize: 18,
    padding: 26
  },
  scrollContainer: {
    flex: 1,
    marginBottom: 100
  },
  footer: {
    position: "absolute",
    bottom: 0,
    left: 0,
    right: 0,
    zIndex: 10
  },
  textInput: {
    alignSelf: "stretch",
    color: "#fff",
    padding: 20,
    backgroundColor: "#252525",
    borderTopWidth: 2,
    borderTopColor: "#ededed"
  },
  addButton: {
    position: "absolute",
    zIndex: 11,
    right: 20,
    bottom: 90,
    backgroundColor: "#E91E63",
    width: 70,
    height: 70,
    borderRadius: 35,
    alignItems: "center",
    justifyContent: "center",
    elevation: 8
  },
  addButtonText: {
    color: "#fff",
    fontSize: 24
  }
});
```

### 9. note.jsをcomponentsの中に移動する。

```jsx

// src/components/nots.js
import React, { Component } from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";

export default class Note extends Component {
  render() {
    return (
      <View key={this.props.keyval} style={styles.note}>
        <Text style={styles.noteText}>{this.props.val.noteText}</Text>

        <TouchableOpacity
          onPress={this.props.deleteMethod}
          style={styles.noteDelete}
        >
          <Text style={styles.noteDeleteText}>D</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  note: {
    position: "relative",
    padding: 20,
    paddingRight: 100,
    borderBottomWidth: 2,
    borderBottomColor: "#ededed"
  },
  noteText: {
    paddingLeft: 20,
    borderLeftWidth: 10,
    borderLeftColor: "#E91E63"
  },
  noteDelete: {
    position: "absolute",
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#2980b9",
    padding: 10,
    top: 10,
    bottom: 10,
    right: 10
  },
  noteDeleteText: {
    color: "white"
  }
});
```


ところで、このメモアプリ一つ欠点がある。
それはアプリを閉じるとメモが消えてしまうこと笑
そこでredux-persistを使いstoreを永続化（スマホのデータベースに保存）することにより解決する。
さあ次のチュートリアルに進もう。
あと少しで君もRNマスターだ。
世界一シンプルなReact-native + Reduxチュートリアル３

## 最後に
４つもファイルが増えました。めんどくさいですね。
本当にredux必要ですか？
unstatedなら30分で基本的な使い方マスターできますよ。
ただ、大規模になればなるほど、疎結合であり、スパゲッティになりにくいreduxの強みが生きてきます。
とてもよくスケールするので、他人の書いたコードを気にせずに自分のコードを書けるのでストレスが貯まりません。

reduxのことは嫌いになってもReactNativeのことは嫌いにならないでください。