10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-08-01

#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.)


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で渡すとき


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にがっちゃんこ


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

let store = createStore(todoApp,window.STATE_FROM_SERVER);

###mapStateToProps
全てのstateを引数にコンポーネントに対して必要なstateをオブジェクトで渡す

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

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

// 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で呼び出す


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する。コンポーネントに渡し、子コンポーネントで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

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


// 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)

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?