react-nativeでiOSアプリ開発者に! 第8回 redux(todoappその1)

みなさんこんにちは、個別指導塾コミット塾長、AWESOME開発担当の船津です。
前回に引き続き、reduxでのアプリ制作を行っていきます。
今回はよくあるtodolistをreduxで作成します。

目次

今回のバージョンのgithubリポジトリ

https://github.com/keisukefunatsu/app-education/tree/2a8e0fca939489207f5412f6a43dd7faf71f0f30/TodoList

一回目ではタスクの追加までを実践してみようと思います。

参考にしたのはこちらの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


import React, { Component } from 'react';
import App from './app/containers/app'
import {
  AppRegistry,
} from 'react-native'

AppRegistry.registerComponent('TodoList', () => App);

containers/app.js

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

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

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

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


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

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リストのタスク完了を実装していきましょう。

モバイルバージョンを終了
トップへ戻る