kindle本「React + Redux入門」のまとめ。
点数
82点
感想
とてもわかりやすかった。
特にreact-reduxの説明が段階的になっていて、なぜそのようになるかを理解することができた。
Reduxの仕組み
状態をStoreと呼ばれるJavaScriptオブジェクト(データだけでなくメソッドも持っている)で管理する。
Storeの変更は事前に定義されたActionと呼ばれるJavaScriptオブジェクトをReducerと呼ばれる関数に渡す(Storeのdispatchメソッドをコール)必要がある。
なぜReactと相性が良いのか
Reactでは各コンポーネントがstateを保持しているため、コンポーネントが増えるとstateも増える。
すると、stateの依存関係が複雑になり、stateを変更するためのメソッドをどこに定義するかも問題になる。
Reduxを使うと、これらを1箇所で管理できるようになる。
状態の変更
StoreのdispatchメソッドにActionオブジェクトを渡すと、Reducer関数が実行され、新しいStoreオブジェクトが返却される。
Reduxの導入
ReactアプリにReduxを追加する
yarn add create-react-app
npx create-react-app app
cd app; yarn add redux react-redux
touch src/reducers.js
import { combineReducers } from 'redux';
export function todos(state = {
list: []
}, action) {
switch (action.type) {
case "ADD_TO_DO":
state.list = state.list.concat(action.todo);
return Object.assign({}, state);
case "REMOVE_TO_DO":
state.list = state.list.filter(todo => {
return (action.todo != todo);
});
return Object.assign({}, state);
default:
return state;
}
}
export default combineReducers({
todos
});
combineReducers
2つ以上の状態を管理したい場合、Reducerを複数作成し、combineReducers関数に渡す。
ReactのmapStateToPropsではstate.nameがstate.Reducer名.nameのように1階層増えるので注意。
https://qiita.com/usagi-f/items/ae568fb64c2eac882d05
touch src/actions.js
export function addToDo(todo) {
return {
type: 'ADD_TO_DO',
todo
}
}
export function removeToDo(todo) {
return {
type: 'REMOVE_TO_DO',
todo
}
}
touch src/store.js
import { createStore } from 'redux';
import reducers from './reducers';
const store = createStore(reducers);
export default store;
※ Storeは別ファイルにすることが多い。
ここまででReduxの準備は完了。
Reactには触れていない点に注目。
react-reduxを追加する
index.js
react-reduxのProviderコンポーネントのstore属性を使うと、子コンポーネントからStoreにアクセスできるようになる。
import { Provider } from "react-redux";
import store from './store';
// 〜省略〜
<Provider store={store}>
<App />
</Provider>,
App.js
react-reduxのconnext関数を使うと、コンポーネントからStoreにアクセスできるようになる。
import { connect } from "react-redux";
// 〜省略〜
export default connect()(App);
ただし、これだけでは個別のデータにアクセスすることはできない。
propsにStoreを反映させるには、mapStateToProps関数を定義してconnect関数に渡す必要がある。
mapStateToPropsは引数に現在のStoreが渡される。
const mapStateToProps = state => {
return {
todos: state.todos.list // combineReducersを使っているのでtodosが間に入る
}
};
export default connect(mapStateToProps)(App);
さらに、Storeの変更をできるようにするには、ActionをReducerに渡す必要がある。
実はProviderの子要素でconnect()したコンポーネントは、this.props.dispatchでStoreのdispatchメソッドをコールすることができる。
import { addToDo, removeToDo } from './actions';
// 〜省略〜
<button onClick={() => this.props.dispatch(addTodo(this.state.input))}>追加</button>
this.props.dispatchは冗長な記述になってしまうので、mapDispatchToPropsという関数を定義するのが一般的。
mapDispatchToPropsは引数にdispatch関数が渡される。
const mapDispatchToProps = dispatch => {
return {
onAddToDo(todo) {
dispatch(addToDo(todo))
},
onRemoveToDo(todo) {
dispatch(removeToDo(todo))
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
コメント