React×GoでTodoリスト作ってみた

基本の復習も兼ねてTodoリストを作りました。
Reactでは、非同期でHTTP通信を行うために[axios](https://github.com/axios/axios)を使っています。
Goでは、[GORM](https://gorm.io/ja_JP/docs/index.html)と[echo](https://echo.labstack.com/guide)を使っています。

##React

```React:Todo.js
import React from 'react';
import InputField from './InputField';
import List from './List';
import http from './http';

class Todo extends React.Component {
  constructor() {
    super();
    this.state = {
      text: "",
      lists: [],
    }
  }

  componentDidMount() {
    this.getTodoList();
  }

  getTodoList = () => {
    return http
      .get('/todo')
      .then((response) => {
        this.setState({ lists: response.data })
      })
      .catch(error => {
        console.log(error)
      })
  }

  handleChange = e => {
    this.setState({ text: e.target.value })
  }

  handleSubmit = () => {
    if (this.state.text === "") {
      return window.alert("入力してください")
    }
    return http
      .post('/todo', {
        text: this.state.text
      })
      .then(() => {
        this.setState({ text: "" });
        this.getTodoList();
      }
      )
      .catch(error => {
        console.log(error)
      })
  }

  handleDelete = (list) => {
    return http
      .delete(`/todo/${list.id}`)
      .then(() =>
        this.getTodoList()
      )
      .catch(error => {
        console.log(error)
      })
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>Todo</h1>
        </div>
        <InputField
          text={this.state.text}
          handleChange={this.handleChange}
          handleSubmit={this.handleSubmit}
        />
        <List
          lists={this.state.lists}
          handleDelete={this.handleDelete}
        />
      </div>
    )
  }
}

export default Todo;
```

```React:InputField.js
import React from 'react';

export default function InputField(props) {
  return (
    <div className="todo-input-field">
      <input placeholder="入力しよう" value={props.text} onChange={e => props.handleChange(e)}></input>
      <button onClick={props.handleSubmit}>保存</button>
    </div>
  )
}
```

```React:http.js
import axios from 'axios';

const API_HOST = process.env.REACT_APP_API_HOST || 'http://localhost:〇〇〇〇';

const http = axios.create({
  baseURL: API_HOST,
});

export default http;
```

##Go

```Go:main.go
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"

	"パスに合わせて他のパッケージをimport"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

const defaultPort = "〇〇〇〇"

func port() string {
	p := os.Getenv("PORT")
	if p != "" {
		return ":" + p
	}
	return ":" + defaultPort
}

func main() {
	connStr := fmt.Sprintf(
		"%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True",
		"DBUSER",
		"DBPASSWORD",
		"DBPROTOCOL",
		"DBNAME",
	)

	db, err := gorm.Open("mysql", connStr)
	if err != nil {
		log.Fatal(err)
	}

	h := handler.New(db)

	server := &http.Server{
		Addr:    port(),
		Handler: h,
	}

	go func() {
		stop := make(chan os.Signal, 1)
		signal.Notify(stop, os.Interrupt)

		<-stop
		log.Println("Shutting down...")

		if err := server.Shutdown(context.Background()); err != nil {
			log.Println("Unable to shutdown:", err)
		}

		log.Println("Server stopped")
	}()

	log.Println("Listening on http://localhost" + port())
	if err := server.ListenAndServe(); err != http.ErrServerClosed {
		log.Fatal(err)
	}
}
```

```Go:handler.go
package handler

import (
	"net/http"

	"github.com/jinzhu/gorm"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func New(db *gorm.DB) http.Handler {
	e := echo.New()
	e.Use(middleware.CORS())

	h := &handler{
		DB: db,
	}

	e.GET("/health", h.health)

	e.GET("/todo", h.getTodoLists)
	e.POST("/todo", h.createTodo)
	e.DELETE("/todo/:id", h.deleteTodo)

	return e
}

type handler struct {
	DB *gorm.DB
}

func (h *handler) health(c echo.Context) error {
	return c.JSON(http.StatusOK, map[string]string{"message": "OK"})
}
```

```Go:list.go
package handler

import (
	"net/http"
	"time"

	"github.com/labstack/echo"
)

type List struct {
	ID        uint      `json:"id"`
	Text      string    `json:"text"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
}

func (h *handler) getTodoLists(c echo.Context) error {
	var lists []List
	err := h.DB.Find(&lists).Error
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, lists)
}

func (h *handler) createTodo(c echo.Context) error {
	var list List
	err := c.Bind(&list)
	if err != nil {
		return err
	}

	err = h.DB.Create(&list).Error
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, list)
}

func (h *handler) deleteTodo(c echo.Context) error {
	var list List
	paramID := c.Param("id")

	err := h.DB.Where("id=?", paramID).Delete(&list).Error
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, list)
}
```

本来であれば、type ~ struct　構造体を他のパッケージからインポートするのがベストな気がするのですが、importがうまくできなかったので、list.goにまとめて書きました。
この問題は解決したい、、、

##DB
todoというデータベースの中にlistsテーブルがあり、中身は画像のようになってます。
<img width="564" alt="スクリーンショット 2020-04-21 20.25.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320179/b68d77aa-0ac8-dbc8-2ff0-e2e8655a66c8.png">

##Todoリスト
<img width="977" alt="スクリーンショット 2020-04-21 20.29.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320179/2e492780-f0b7-c1e9-6579-8ba23c0434e9.png">

このような形で簡単なTodoリストが作成できると思います。