Reduxが難しいけど、簡単な話

#React勉強会

Reduxを使ったTodoアプリ
https://github.com/mackay-isogawa/redux-todo.git

##Redux
肝は「action」「reducer」「store」でデータの一連の流れ

###Action

ただのオブジェクト

"type"プロパティを必ず持つ

後にこのtypeを参照してactionを呼び出す

actionに入ってくるのは、componentでキャッチしたstateとaction.type

store(reducer関数)に渡したいデータを入れる
=>reducerにてaction.methodNameでデータにアクセス可能

ex.)

```javascript

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,
  }
}
```

###Reducer
ただのFunction.

入力されるstateに対して新しいstateを返す

reducerに渡されるStateはStoreを作成する時に決定される

どこからStateが来るかとういうと、CreateStoreで渡すとき

```javascript

const todos = (state = [], action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return [
                ...state,
                {
                    id: action.id,
                    text: action.text,
                    completed: false
                }
            ]

        case 'TOGGLE_TODO':
            return state.map(todo =>
                (todo.id == action.id) ? {
                    ...todo,
                    completed: !todo.completed
                } :
                todo
            )
        default:
            return state;
    }
}

export default todos;
```



###Store
ReducerをReactにがっちゃんこ

```javascript

import todoApp from '../reducers';
import { createStore } from 'redux';

let store = createStore(todoApp,window.STATE_FROM_SERVER);
```
###mapStateToProps
全てのstateを引数にコンポーネントに対して必要なstateをオブジェクトで渡す

Storeのデータはここに流れ着く

ここで、ストアのデータを加工して（もしくは無加工で）returnする

```javascript
// container.js
// stateがstore
// state.name = 'mackay'

const mapStateToProps = state => {
	return{
		return {
			name : state.name
		}
	}
}

const mapDipatchToProps = dispatch => {
	//hogehoge
}

export default connect(
    mapStateToProps,
    mapDipatchToProps
)(TodoList)


// TodoList.jsx

const Todolist = (name) => {
	console.log(name); // mackay
}
```

###mapDispatchToProps
全てのDispatchを引数に全てのコンポーネントに対してDispatchを渡す

dispatchするactionをfunctionで呼び出す


```javascript

const mapStateToProps = state => {
   // hogehoge
}

const mapDipatchToProps = dispatch => {
    return {
        onTodoClick: id => {
            dispatch(toggleTodo(id))
        }
    }
}

const VisibleTodoList = connect(
    mapStateToProps,
    mapDipatchToProps
)(TodoList)

export default VisibleTodoList;


// TodoList.jsx
const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo => (
      <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} />
    ))}
  </ul>
);



```
###Connect
"map〜"をコンポネートに実際に適用させる

###Provider
"Connect"で作成したコンポーネント をProviderでラッピングする

Providerのstore属性に渡すStoreにより、接続先Storeを管理


###Dipatch
actionを呼び出す

###combineReducer
複数にreducerを一つにまとめる

##ComponentとConatiner
containerは処理の流れ

componentはビューのみ

まずcontainerを呼び出して、componentを間接的に呼び出す。

"Root"から様々なcomponentを呼び出す例

- RootContainer
	- RootComponent
		- ConatinerA
			- ComponentA
		- ConatinerB
			- ComponentB
				- ConatinerC
					- ComponentC 



containerでstoreを実際に適用させる

componentで使いたいstateは、mapStateToPropsとmapDispatchToPropsでconnectする


###example
- index.js
- store.js
- component
	- App.jsx
	- List.jsx
- container
	- List.js
- reducers
	- idnex.js
- actions 
	- index.js

1. index.jsからstoreを呼び出し。store内でreducerをcreateStoreする。<Provider>コンポーネントに渡し、子コンポーネントでstoreが使えるように
2. index.jsからccomponent/App.js呼び出し
3. component/App.jsxからcontainer/List.js呼び出し
4. container/List.jsではactions/index.jsを呼び出す。List.js内でmapStateToPropsで（store内で生成した）storeの状態を、mapDispatchToPropsでdispatchするactionを関数化。component/List.jsxにconnectする
5. coponent/List.jsxではcontainer/List.jsから渡されるstateとdipatchを受け取り、コンポーネントをレンタリング
6. 表示成功（たぶん）
	
	(src/)inedx.jsから直接containerを呼び出してもいい。
	しかし、美しくない。
	処理が肥大した際に、追加がめんどい。
	conatinerの関心はあくまでreducerであり、いわゆるコンポーネント（view）はcomponentに任せるべきである
	
	
###Conatinerは分割すべし
一つのcontainerに複数のコンポーネントの呼び出しがあり、それぞれのコンポーネントに対してmap-Toをしている場合、ファックすることになる

コンポーネントの呼び出しに対して、新たなcontainerを作り、そこでcomponentを呼び出す

######Bad Container
`badcontainer.js`

```javascript
import ~~

const BadContainer = () => {
	<div>
		<ComponentA
			name= {this.props.nameA}
			hangle= {this.props.handleA1}
		/>
		<ComponentB
			name= {this.props.nameB}
			hangle= {this.props.handleB1}
		/>
	</div>
}

const mapStateToProp = state => {
	return {
		stateA : state.a.name,
		stateB : state.b.name
	}
}
const mapDispatchToProps = dispatch => {
  return {
    handlerA1() {
      dispatch(actions.actionA1())
    },
    handlerB1() {
      dispatch(actions.actionA1())
    },
    fetch() {
      dispatch(actions.fetchData())
    }
  }
}

) 
 
export default connect(mapStateToProps, mapDispatchToProps)(BadContainer)

```
######Good Container

`GooContainer.js`

```javascript

// GoodContainer.jsx
import ContainerA from './ContainerA'
import ContainerB from './ContainerB'
import * as actions from '../actions'
 
const GoodContainer = () => {
  <div>
    <ContainerA />
    <ContainerB />
  </div>
}
 
const mapDispatchToProps = dispatch => {
  return {
    fetch() {
      dispatch(actions.fetchData())
    }
  }
}
 
export default connect(()=> { return {} }, mapDispatchToProps)(GoodContainer)
 
// ContainerA.jsx
 
import ComponentA from '../components/ComponentA'
import * as actions from '../actions'
 
const mapStateToProps = state => {
  return {
    name: state.a.name,
  }
}
 
const mapDispatchToProps = dispatch => {
  return {
    handler1() {
      dispatch(actions.actionA1())
    },
    handler2() {
      dispatch(actions.actionA2())
    },
  }
}
 
export default connect(mapStateToProps, mapDispatchToProps)(ComponentA)
 
// ContainerB.jsx
 
import ComponentB from '../components/ComponentB'
import * as actions from '../actions'
 
const mapStateToProps = state => {
  return {
    name: state.b.name,
  }
}
 
const mapDispatchToProps = dispatch => {
  return {
    handler1() {
      dispatch(actions.actionB1())
    },
    handler2() {
      dispatch(actions.actionB2())
    },
  }
}
 
export default connect(mapStateToProps, mapDispatchToProps)(ComponentB)

```