ReduxのサンプルのTodoリストを真似てみる。Part.1の続きです。
0.留意点
- Redux ExampleのTodo Listをはじめからていねいに(1)を参考にさせて頂いて、Reduxのコードを書いた。
- 初心者の覚書です。
- 自分の環境で動くように参考にしたコードを適当に修正している。
- Windows10 64bit , PowerShellなどで動かしている。
- 見栄えを若干よくする為にbootstrap4を利用している。
1.Todoの配列でTodoリストを作る
reducersを修正
todoをtodosに変更 stateをtodoの配列にする
ファイル名を reducers/index.jsからreducers/todos.js に変更
const todos = (state=[], action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
id: action.id,
text: action.text
}
default:
return state;
}
}
export default todos;
actions/index.jsはあたらしく作り直す。
combineReducers関数を使って、複数のreducers(複数のロジック)をまとめて扱えるようにする。
たとえば、
reducers/hoge.js のなかで、export default hoge
reducers/fuga.js のなかで、export default fuga
し、actions/index.jsで、importして
combineReducers({todos, hoge, fuga})
としておくと、stateオブジェクトのtodosプロパティ、hogeプロパティ、fugaプロパティとしてアクセスできる。この時点ではtodosだけだが、先のことを考えて準備しておく。
import todos from './todo'
import { combineReducers } from 'redux';
const todoApp = combineReducers({todos});
export default todoApp;
コンポーネントをつくる
この辺から、bootstrapを使う。クラス名の指定でできるだけ対応。React、Reduxは関係しない。
コンポーネントの前にまず、ルートのhtmlファイルを修正
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React,ReduxでTodoリストを作ろう</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body >
<!-- classでBootsrapに関する設定 -->
<div class="mx-auto w-50 mt-3" id="root"></div>
<script src="dist/index.js"></script>
</body>
</html>
Todo一つ一つに対応するTodoコンポーネント
import React from 'react';
import PropTypes from 'prop-types';
//classNameでBootsrapに関する設定
const Todo = ({text})=>(
<li className="list-group-item">{text}</li>
)
Todo.propTypes ={
text:PropTypes.string.isRequired
}
export default Todo;
複数のTodoコンポーネントを格納するTodoListコンポーネント
import React from 'react';
import PropTypes from 'prop-types';
import Todo from './Todo';
//classNameでBootsrapに関する設定
const TodoList = ({todos})=>(
<ul className="list-group mx-auto p-3 bg-info rounded" style={{maxWidth:'36rem'}}>
{todos.map(
(todo)=>(<Todo key={todo.id} {...todo} />)
)}
</ul>
)
TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired
}
export default TodoList
コンテナでstateとコンポーネントを結びつける
VisibleTodoListコンテナのなかで、state.todosを TodoListのpropsに関連付ける。
そのために、React-Reduxのconnect関数を使う。
import React from 'react';
import { connect } from 'react-redux';
import TodoList from '../components/TodoList'
function mapStateToProps(state){
return {
todos:state.todos
}
}
const VisibleTodoList = connect(mapStateToProps)(TodoList);
export default VisibleTodoList;
Appコンポーネントを修正
Appコンポーネントでは、上記のVisibleTodoListコンテナを読み込む。
import React from 'react';
import VisibleTodoList from '../containers/VisibleTodoList'
//classNameでBootsrapに関する設定
const App = () => (
<div>
<h1 className="text-center alert alert-info ">Todoリスト</h1>
<VisibleTodoList />
</div>
)
export default App;
エントリーポイントのindex.jsを修正
ここまでの変更に合わせて、import関係、モジュール参照名などを修正。
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import todoApp from './reducers'
import { createStore } from 'redux';
import App from './components/App'
import addTodo from './actions'
let store = createStore(todoApp);
store.dispatch(addTodo('Todo 1番目'));
store.dispatch(addTodo('Todo 2番目'));
store.dispatch(addTodo('Todo 3番目'));
store.dispatch(addTodo('Todo 4番目'));
store.dispatch(addTodo('Todo 5番目'));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
npm run build
npm run start
2.Todoの新規追加のフォームをつくる
初めの読み込み時に、input変数にDOMへの参照が格納される。
AddTodo コンポーネントにdispatchを引数で渡す。
新規追加ボタンをクリックすると、コールバックで、ユーザー入力のテキストを用いたactionを引数にしてdispatch関数を実行。
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import addTodo from '../actions/index.js';
let AddTodo = ({ dispatch }) => {
let input;
//className,htmlForなどはbootstrapのため
return (
<div className="form-group">
<label htmlFor="todo_name">Todoの名称</label>
<input type="text" className="form-control" id="todo_name" placeholder="Todoの名称" ref={(node) => { input = node }} />
<button className="btn btn-primary btn-lg btn-block mt-1" onClick={() => { dispatch(addTodo(input.value)); input.value="" }}>新規追加</button>
</div>
)
}
AddTodo = connect()(AddTodo);
export default AddTodo;
Appコンポーネントを修正
import React from 'react';
import VisibleTodoList from '../containers/VisibleTodoList'
import AddTodo from '../containers/AddTodo'
//classNameでBootsrapに関する設定
const App = () => (
<div>
<h1 className="text-center alert alert-info ">Todoリスト</h1>
<VisibleTodoList />
<AddTodo />
</div>
)
export default App;