Reduxとは?
stateを容易に管理するためのフレームワーク。
Reactは親コンポーネントから子コンポーネントへpropsを通してデータ(state)を渡している。
となると、親子関係にないコンポーネント間でのstateの受け渡しがかなり難しい。
→一番上のコンポーネントに全てのstateを管理し、それを子コンポーネントに順次伝えていく。

説明に関しては、以下のURLの記事が最も分かりやすい。
https://qiita.com/kiita312/items/49a1f03445b19cf407b7
根本的に、「変化」と「非同期」を"同時"にコントロールするのは人間には無理がある。
Reactはその問題に対してViewレイヤーで非同期と直接DOM操作を取り除くことで解決したけども、
state(状態)の管理は開発者に委ねられたままなので、Reduxはそこを解決するためにある。
そもそもstateとは?
変数みたいなやつ(ブリボンの感覚です。)
「状態を管理するState」とかいう表現で説明されるが、正直よく分からない。
とりあえず、変数とかデータとかいう認識で(railsでいうmodelからカラムを引っ張ってくる的な)。
そのコンポーネント内でのみ使うことができる。
コンポーネント外からstateを変更することはできない。
Reduxの流れ
1ユーザーの入力からactionを作成する
2作成されたactoinをstoreへdispatch
3actionとstateを元にreducerが新しいstateに変更する
→actionとstateをreducerに渡し、新しいstateを作成する
4新しく作られたstateをstoreに保存する
→reducerが新たなstateを作成し、storeへ保存する
Reduxに必要な知識(ブ→ブリボン、公→公式)
・state
上述参考。
・Action
ブ:storeにdispatchを通してぶち込まれるもの。
公:アプリケーションからの情報をstoreへ送る為のオブジェクト。
store.dispatch()でstoreへ送られる。
ブ:store.jsで書いているものは、actionCreator。
そのactionCreatorから概念的なactionなるものが作成され、dispatchによりstoreへ送られる。
・Reducer
ブ:storeにdispatchされたactionとstore内のstateから新たなstateを生成してくれるやつ。
公:stateを変更してくれるもの。actionとstateから、新しいstateを作成して返すメソッドです。
禁止事項
・引数のstate, actionインスタンスの値を変更する
・副作用をおこす(APIを呼んだり、ルーティングを変えるなどなど)
・毎回値が変わるもの(Date.now() や Math.random())を扱う
・Store
ブ:Reactの各コンポーネントでデータ(state)をやり取りするためのもの。railsでいうmodelというかデータベースのようなもの。アプリケーションで1つのみ存在。
Storesの役割は、
・stateを保持する
・stateへアクセスするためのgetState()を提供する
・stateを更新するためのdispatch(action)を提供する
・リスナーを登録するためのsubscribe(listener)を提供する
・純粋関数(pure function)
ブ:何が純粋か非純粋かよく分からん。高校生か。
不明点
・子reducerの返したstateを親reducerがまとめて一つのツリー状のstateを返す
→「ツリー状のstate」とは?
実装
githubのコード:https://github.com/ryubb/redux-app
git cloneをする
redux-appのレポジトリにcdする
yarn install
yarn start
↑これで動きます
下準備
npm react-create-app [任意のアプリ名]
→reactでプロジェクト(アプリケーション)を立ち上げるコマンド
react add redux react-redux
→reduxとreact-reduxという2つのパッケージが必要
reducer
import { combineReducers } from 'redux';
export function todos(state={
list: {}
}, action) {
switch(action.type) {
case "ADD_TO_DO":
// Object.assignの動きは良く分からないので、とりあえずstateを生成していると思っていてください。
return Object.assign({}, state, {
list: state.list.concat(action.todo)
});
case "REMOVE_TO_DO":
return Object.assign({}, state, {
list: state.list.filter(todo => {
if(action.todo != todo) {
return true;
} else {
return false;
}
})
});
default:
return state;
}
}
export default combineReducers({
todos,
});
action
export function addToDo(todo) {
return {
type: 'ADD_TO_DO',
todo
}
}
export function removeToDo(todo) {
return {
type: 'REMOVE_TO_DO',
todo
}
}
store
import { createStore } from 'redux';
import reducers from "./reducers";
const store = createStore(reducers);
export default store;
index.js(index.htmlにバインド?している大元のファイル)
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './stores';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
App.js(実際に実装していくファイル)
import React, { Component } from 'react';
import { connect } from "react-redux";
import { addToDo, removeToDo } from './actions';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super();
this.state = {
input: ""
};
}
render() {
return (
<div className="App">
{/* <header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header> */}
<ul>
{this.props.todos.map(todo => {
return (
<li key={todo}>
<span>{todo}</span>
<button onClick={() => this.props.onRemoveToDo(todo)}>削除</button>
</li>
)
})}
</ul>
<input type="text" onChange={e => this.setState({input: e.target.value})} />
<button onClick={() => this.props.onAddToDo(this.state.input)}>
追加
</button>
</div>
);
}
}
const mapDispatchToProps = dispatch => {
return {
onAddToDo(todo) {
dispatch(addToDo(todo))
},
onRemoveToDo(todo) {
dispatch(removeToDo(todo))
}
}
};
// これを記述することで、this.props.dispatchが使える?
const mapStateToProps = state => {
return {
todos: state.todos.list
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);