みなさんこんにちは、個別指導塾コミット塾長、AWESOME開発担当の船津です。
前回に引き続き、reduxでのアプリ制作を行っていきます。
今回はよくあるtodolistをreduxで作成します。
目次
今回のバージョンのgithubリポジトリ
一回目ではタスクの追加までを実践してみようと思います。
参考にしたのはこちらの2つ
公式サイト
http://redux.js.org/docs/basics/ExampleTodoList.html
とてもわかり易く、順番に説明されているのでとても参考になりました。
http://qiita.com/xkumiyu/items/9dfe51d2bcb3bdb06da3
reduxはハマりどころがいっぱいいっぱい
触ってみての感想ですが、reduxは習得に時間がかかります。またハマりどころもたくさんあります。今回はファイル毎にどこでハマったか、どのようにして回避したかを説明しながら進めていきます。
reduxを使うときは動作確認しながら進めていこう
いろいろな資料がありますが、作るファイルが多いので、どこから手を付けて良いのか迷いがちです。
まずはstoreの部分を作って最低限の動作を確認しよう。
例えばこんな感じです。
let store = createStore(todo)
export default class App extends Component {
constructor(props) {
super(props)
}
render() {
return (
<Provider store={store}>
<TodoApp/>
</Provider>
)
}
}
export default class TodoApp extends Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
const { text } = this.props;
return (
<View>
<Text>
{text}
</Text>
<TouchableHighlight onPress={() => {store.dispatch({ type : 'HELLO'})}}
activeOpacity={75 / 100}
underlayColor={"rgb(210,210,210)"}>
<Text>Press</Text>
</TouchableHighlight>
<View>
)
}
}
export default function todo( state = {}, action = {}) {
switch (action.type) {
case 'HELLO':
return {
text: 'hello'
}
default:
return state
}
}
function mapStateToProps(state) {
return {
text: state.text
}
}
export default connect(mapStateToProps)(TodoApp)
ここまで正しければ、「press」を押すと、’hello’と表示されるかと思います。
どんなアプリでもそうですが、最低限の実装をして→機能別にファイル分割の流れで進めていきましょう。
それでは作っていきましょう。
ファイル一覧
- index.ios.js アプリの登録を行います。
- components/
- TodoList.js storeから受け取ったtodosをmapで処理して個別に表示していきます。
- Todo.js Todo.jsの中で繰り返し呼ばれるコンポーネントです。todoリストの本体とも言える部分です。
- containers/
- TodoApp.js connect関数を使ってreducerのstateをreact-nativeでも使えるように関連付けます。
- app.js アプリのルートになる部分です。storeを作成します。
- reducers/
- todos.js actionに対応したstateを返します。
- index.js store作成時に呼ばれる関数をまとめて記述します。
- actions/
- index.js アクションを作成します。
それではひとつずつ見ていきましょう。
index.ios.js
- アプリを登録します。
import React, { Component } from 'react';
import App from './app/containers/app'
import {
AppRegistry,
} from 'react-native'
AppRegistry.registerComponent('TodoList', () => App);
containers/app.js
- storeを作成します。
- storeがグローバルになってますが、気にせず進めましょう。
- 今回はcreateStoreの引数に複数のreducerをまとめたものを指定します。
- といったもまだtodosしか作成されていませんが、、、
import React, { Component, } from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer from '../reducers';
import TodoApp from './TodoApp';
import todo from '../reducers/todos'
store = createStore(reducer)
export default class App extends Component {
constructor(props) {
super(props)
}
render() {
return (
<Provider store={store}>
<TodoApp/>
</Provider>
)
}
}
containers/TodoApp.js
- react-nativeとreduxのstateをconnect関数で共有できるようにします。
- 必ずconnectが期待通りの動作をしていることを確認してから進めましょう。
- ここがしっかり動作していないとこの先の全てが動きません。
import React, { Component, } from 'react';
import { connect } from 'react-redux';
import TodoList from '../components/TodoList'
export default class TodoApp extends Component {
static propTypes = {}
static defaultProps = {}
constructor(props) {
super(props)
this.state = {
}
}
render() {
const { todos } = this.props;
return (
<TodoList todos={todos} />
)
}
}
function mapStateToProps(state) {
return {
todos: state.todos
}
}
export default connect(mapStateToProps)(TodoApp)
components/TodoList.js
- todosオブジェクトをmapでtodoに分割して処理していきます。
- 後述のTodoを繰り返し呼んでリストのように表示させます。
- 今回は簡単のためにTextInputで入力した値を直接store.dispatchでreducerに渡しています。
- ここではちゃんとtodosオブジェクトがundefinedになっていないか確認する方が良いでしょう。
import React, { Component, } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableHighlight,
} from 'react-native'
import { addTodo } from '../actions/index';
import Todo from './Todo';
export default class TodoList extends Component {
static propTypes = {}
static defaultProps = {}
constructor(props) {
super(props)
this.state = {
}
}
render() {
const { todos } = this.props;
return (
<View style={styles.container}>
<View>
{todos.map((todo, index) =>
<Todo key={index} text={todo.text} {...todo} />
)}
<TextInput style={styles.input} placeholder={ 'tasks?' } placeholderTextColor={"rgba(198,198,204,1)"} onChangeText={(text) => {this.setState({text})}}
onSubmitEditing={() => {
store.dispatch(addTodo(this.state.text))
this.setState({text: ''})
}}
value={(this.state && this.state.text) || ''}
/>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
},
input: {
height: 30,
width: 100,
borderWidth: 1,
borderColor: "rgba(0,0,0,0.5)",
borderRadius: 5,
textAlign: 'center',
}
});
components/Todo.js
- TodoListで呼ばれる部分です。今回はTodoのテキストを表示させるだけです。
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableHighlight,
} from 'react-native'
export default class Todo extends Component {
render(){
const { text } = this.props
return (
<View>
<Text>
{text}
</Text>
</View>
)
}
}
reducers/todos.js
- actionに対してstateがどのように変化するかを記述します。
- 公式サイトのコピーなので詳しくは公式サイトをご覧ください。
export default function todo( state = {}, action = {}) {
switch (action.type) {
case 'ADD_TODO':
return {
id: action.id,
text: action.text,
completed: false,
}
case 'TOGGLE_TODO':
if (state.id !== action.id){
return state
}
return Object.assign({},state, {
completed: !state.completed
})
default:
return state
}
}
export default function todos( state = [], action = {}) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
todo(undefined, action)
]
case 'TOGGLE_TODO':
return state.map(t =>
todo(t, action)
)
default:
return state
}
}
reducers/index.js
- reducerをまとめます。今回はtodosだけですが、次回から複数になるのでcombineReducersでまとめておきます。
import { combineReducers } from 'redux';
import todos from './todos';
const reducer = combineReducers({
todos,
});
export default reducer
actions/index.js
- こちらも公式のままですね。公式サイトをご覧ください。
let nextTodoId = 0;
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
id: nextTodoId++,
text
}
}
export const setVisibilityFilter = (filter) => {
return {
type: 'SET_VISIBILITY_FILTER',
filter
}
}
export const toggleTodo = (id) => {
return {
type: 'TOGGLE_TODO',
id
}
}
ここまで出来ると
こんな感じになります。

おしまい
長くなってしまいましたが、いかがだったでしょうか?
次回はdispatchをまとめたりtodoリストのタスク完了を実装していきましょう。



