こちらの記事のRedux対応版(改修の余地あり)です。個人的な備忘メモなので解説あまりしてません。
##準備
npm install --save redux react-redux redux-thunk
ディレクトリ構造
Laravelのresourcesのjsの中を以下のようにしました。
Redux-Wayとも呼べない中途半端なもの。結局containerは利用しませんでした。
実装
app.js
Laravelにおけるエンドポイント。まあ、初期jsを読み込んでるだけ。
require('./index.js');
index.js
index.jsってのを作りました。
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import createStore from './createStore';
import TodoApp from './components/TodoApp';
const store = createStore();
ReactDOM.render(
<Provider store={store}>
<TodoApp />
</Provider>,
document.getElementById('todoApp')
);
components/Todo.js
コンポーネントとしては一番根っこになるTodoApp。
中のテーブル行のレンダリングで、更に子の<RenderList>を呼んでいる。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import createStore from '../createStore';
import * as actions from '../actions/Todo';
import RenderList from './RenderList';
class TodoApp extends Component {
constructor(){
super();
this.inputChange = this.inputChange.bind(this);
this.addTodo = this.addTodo.bind(this);
}
inputChange(event){
switch(event.target.name){
case 'todo':
this.props.changeTodo(event.target.value);
break;
default:
break;
}
}
addTodo(){
if(this.props.state.todo == ''){
return;
}
this.props.addTodo(this.props.state.todo);
this.props.changeTodo('');
}
render() {
return (
<React.Fragment>
{/* form */}
<div className="form-group mt-4">
<label htmlFor="todo">新規Todo</label>
<input type="text" className="form-control" id="todo" name="todo" value={this.props.state.todo} onChange={this.inputChange}/>
</div>
<button type="submit" className="btn btn-primary" onClick={this.addTodo}>登録</button>
{/* table */}
<table className="table mt-5">
<thead>
<tr>
<th>ID</th>
<th>タスク</th>
<th>完了</th>
</tr>
</thead>
<tbody>
<RenderList/>
</tbody>
</table>
</React.Fragment>
);
}
componentDidMount(){
this.props.getTodos();
}
}
const mapStateToProps = state => {
return {
state: state.todos,
}
}
const mapDispatchToProps = dispatch => {
return {
getTodos: () => dispatch(actions.getTodos()),
addTodo: (todo) => dispatch(actions.addTodo(todo)),
changeTodo: (todo) => dispatch(actions.changeTodo(todo))
}
}
export default connect(mapStateToProps,mapDispatchToProps)(TodoApp);
components/RenderList.js
<tr>をレンダリングしている。
Reduxを利用して、バケツリレーせず、TodoAppでもRenderListでも同じ値やメソッドを呼び出している。
import React from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions/Todo';
class RenderList extends React.Component{
constructor(){
super();
this.deleteTask = this.deleteTask.bind(this);
}
deleteTask(todo){
this.props.deleteTodo(todo);
}
render(){
const todolist = this.props.state.todos.map(todo => {
return (
<tr key={todo.id}>
<td>{todo.id}</td>
<td>{todo.title}</td>
<td><button className="btn btn-secondary" onClick={() => this.deleteTask(todo)}>完了</button></td>
</tr>
);
});
return (todolist);
}
}
const mapStateToProps = state => {
return {
state: state.todos,
}
}
const mapDispatchToProps = dispatch => {
return {
deleteTodo: (todo) => dispatch(actions.deleteTodo(todo)),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(RenderList);
createStore.js
createStoreってファイル作る文化はないのだけど、作ってみました。storeディレクトリ作ってもいいのかも。
thunkの利用を宣言しています。
import { createStore as reduxCreateStore, applyMiddleware, combineReducers } from "redux";
import { todoReducer } from "./reducers/Todo";
import thunk from 'redux-thunk';
export default function createStore() {
const store = reduxCreateStore(
combineReducers({
todos: todoReducer,
}),
applyMiddleware(
thunk,
)
);
return store;
}
reducers/Todo.js
reducer。基本的にTODOのCRUD用ですが、CHANGE_TODOだけ、input の値更新用となっている。
const initialState = {
todos: [],
todo: ''
}
export const todoReducer = (state = initialState, action) => {
// console.log(action.todos);
switch (action.type){
case 'GET_TODOS':
const getState = Object.assign({}, state);
getState.todos = action.todos;
return getState;
case 'ADD_TODO':
const addState = Object.assign({}, state);
addState.todos = action.todos;
return addState;
case 'DELETE_TODO':
const delState = Object.assign({}, state);
delState.todos = action.todos;
return delState;
case 'CHANGE_TODO':
const changeState = Object.assign({}, state);
changeState.todo = action.todo
return changeState;
default:
return state;
}
}
actions/Todo.js
action。changeTodoはUpdateではなくてinputのonChange()に対応する用のメソッドとした(もっといい方法あると思う)。
thunk使って、actionの中で非同期処理してます。記述は最低限です。
import axios from 'axios';
export const addTodo = (todo) => async dispatch => {
const response = await axios.post('/api/add',{title: todo});
dispatch({
type: 'ADD_TODO',
todos: response.data
});
}
export const getTodos = () => async dispatch => {
const response = await axios.get('/api/get');
dispatch({
type: 'GET_TODOS',
todos: response.data
});
}
export const deleteTodo = (todo) => async dispatch => {
const response = await axios.post('/api/del',{id: todo.id});
dispatch({
type: 'DELETE_TODO',
todos: response.data
});
}
//onChange用
export const changeTodo = (todo) => {
return {
type: 'CHANGE_TODO',
todo: todo
}
}
とりあえず以上。