LoginSignup
9
11

More than 5 years have passed since last update.

Laravel5.7 + React + Redux + Thunk

Last updated at Posted at 2018-10-23

こちらの記事のRedux対応版(改修の余地あり)です。個人的な備忘メモなので解説あまりしてません。

準備

npm install --save redux react-redux redux-thunk

ディレクトリ構造

Laravelのresourcesのjsの中を以下のようにしました。
Redux-Wayとも呼べない中途半端なもの。結局containerは利用しませんでした。

スクリーンショット 2018-10-23 15.08.14.png

実装

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
    }
}

とりあえず以上。

9
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
9
11