railsとreactでtodoアプリを作る！（part4）

# 作成するアプリについて
railsとreactを用いたtodoアプリになります。
https://todo-app-rails-react.herokuapp.com/todoes

https://github.com/taiki-nd/todo-app

（part1）

https://qiita.com/taiki-nd/items/aa9d0a03d1950876efab

（part2）

https://qiita.com/taiki-nd/items/41230829bcedd73bdec4

（part3）

https://qiita.com/taiki-nd/items/14ff0faf685905532e0f

（part5）

https://qiita.com/taiki-nd/items/b7b9a6a24f0c99458b2a

# 開発環境
* ruby3.0
* rails6.1(rails6以上必須)
* react17.0.2
* vscode

# アプリの作成手順

1. サーバーサイドの実装
    1. アプリの作成
    1. turbolinksの無効化
    1. モデル＆テーブルの作成
    1. 初期データの作成
    1. top#indexの作成
    1. todoes_controllerの作成
    1. ルーティングの設定
    1. application_controllerの設定
    1. application.html.erbに追記(part1でここまで完了済み)
1. フロントサイドの実装
    1. componentsフォルダの作成
    1. リセットcssと共通cssの設定
    1. index.jsxの記述
    1. フロントの実装準備
    1. App.jsの記述(part2でここまで完了済み)
    1. TodoList.jsの記述(part3でここまで完了済み)
    1. NewTodo.jsの記述
    1. EditTodo.jsの記述

それでは進めていきましょう！

# アプリ作成
## 2. フロントサイドの実装
### 2.7 NewTodo.jsの記述

まずは一気に見た目の調整をしていきましょう！TodoList.jsと似ているのでサクッといきます！

```jsx:app/javascript/components/NewTodo.js
import React from 'react'
import styled from 'styled-components'

export const NewTodo = () => {
  return (
    <>
      <h1>NewTodo</h1>
      <InputAndNew>
        <Input type="text" placeholder="NewTodo..." />
        <NewBtn >AddNewTodo</NewBtn>
      </InputAndNew>
    </>
  )
}

const InputAndNew = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
`

const Input = styled.input`
  font-size: 20px;
  width: 100%;
  height: 40px;
  margin: 10px 0;
  padding: 10px;
`

const NewBtn = styled.button`
  width: fit-content;
  height: 40px;
  background: #ccffcc;
  border: none;
  font-weight: 500;
  margin-left: 10px;
  padding: 5px 10px;
  border-radius: 3px;
  cursor: pointer;
`
```

:::note warn
import styled from 'styled-components'を忘れずに記述しましょう！
:::
以下のようになりましたでしょうか？
![スクリーンショット 2021-11-01 17.54.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645779/abb8898c-966b-365d-98cf-c6bfdb114471.png)

では、機能面の実装に移ります！

まずはinput関連ですね。

```jsx:app/javascript/components/NewTodo.js
import React, { useState } from 'react'
import styled from 'styled-components'

export const NewTodo = () => {

  const initialTodoStatus = {
    id: null,
    content: "",
    is_completed: false
  }

  const [todo, setTodo] = useState(initialTodoStatus)

  const onChangeNewTodo = (e) => {
    const { name, value } = e.target;
    console.log(value) //入力した文字列
    console.log(name) //content 
    setTodo({ ...todo,[name]: value });
　　　　　　　　//todoを展開して、カラム名を指定してvalueに更新します。
  }

  return (
    <>
      <h1>NewTodo</h1>
      <InputAndNew>
        <Input  
          type="text" 
          placeholder="NewTodo..." 
          required
          value={todo.name}
          onChange={onChangeNewTodo}
          name="content" />
        <NewBtn >AddNewTodo</NewBtn>
      </InputAndNew>
    </>
  )
}

//以下省略
```
:::note warn
import React, { useState } from 'react'になってますので気をつけてください！
:::

続けて、AddNewTodoボタンの実装に移ります。

```jsx:app/javascript/components/NewTodo.js
//以上省略
const InputNewTodo = (e) => {
    const { name, value } = e.target;
    console.log(value)
    console.log(name)
    setTodo({ ...todo,[name]: value });
  }

  const onClickSaveTodo = () => {
    const newVal = {
      content: todo.content
      //contentカラムにinputで記述したものを代入しています。
    };
    axios.post('/api/v1/todoes', newVal)
    .then(resp => {
      setTodo({
        id: resp.data.id,
        content: resp.data.content,
        complete: resp.data.complete
      });
      //レスポンスで渡されたデータを代入していきます。
      props.history.push("/todoes");
      //TodoList.jsにpropsとして情報を渡します。
    })
    .catch(e => {
      console.log(e)
    })
  }

  return (
    <>
      <h1>NewTodo</h1>
      <InputAndNew>
      <Input 
          type="text" 
          placeholder="NewTodo..." 
          required
          value={todo.name}
          onChange={InputNewTodo}
          name="content" />
        <NewBtn onClick={onClickSaveTodo}>AddNewTodo</NewBtn>
      </InputAndNew>
    </>
  )
}
//以下省略
```
動くか確認してみましょう！
![スクリーンショット 2021-11-01 20.54.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645779/6769f203-d83c-24c8-4f7a-d109ada740fe.png)
AddNewTodoをクリック！
![スクリーンショット 2021-11-01 20.54.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645779/676e658d-af1e-da2a-9d62-2f435ee13b9d.png)
うまくいってますね！

続いてtoastifyの実装をしていきます！イケてるポップアップを簡単に実装できます！

```jsx:app/javascript/components/NewTodo.js
import axios from 'axios'
import React, { useState } from 'react'
import styled from 'styled-components'
import { toast } from 'react-toastify'
import "!style-loader!css-loader!react-toastify/dist/ReactToastify.css"//toastifyのcss

toast.configure();//toastifyを有効にするには必須（忘れがち）

export const NewTodo = (props) => {

  const notify = () => {
    toast.success("Todo successfully created!", {
      position: "bottom-center",
      hideProgressBar: true
    });
  }

  const initialTodoStatus = {
    id: null,
    content: "",
    is_completed: false
  }

  const [todo, setTodo] = useState(initialTodoStatus)

  const InputNewTodo = (e) => {
    const { name, value } = e.target;
    console.log(value) 
    console.log(name) 
    setTodo({ ...todo,[name]: value });
  }

  const onClickSaveTodo = () => {
    const newVal = {
      content: todo.content
    };
    axios.post('/api/v1/todoes', newVal)
    .then(resp => {
      setTodo({
        id: resp.data.id,
        content: resp.data.content,
        complete: resp.data.complete
      });
      notify();//toastifyの表示タイミング
      props.history.push("/todoes");
    })
    .catch(e => {
      console.log(e)
    })
  }
//以下省略
```
:::note warn
import { toast } from 'react-toastify'
import "!style-loader!css-loader!react-toastify/dist/ReactToastify.css"
toast.configure();
notify();
が追記されています。
:::

:::note warn
toastifyのcssは開発環境のみであれば以下でも問題ないですが、本番環境ではstyle崩れをおこす可能性があります。
import 'react-toastify/dist/ReactToastify.css'
:::

では確認してみましょう！
![スクリーンショット 2021-11-01 21.01.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645779/e80e660a-1cef-d79b-bc5c-1dcfbf0faf00.png)

![スクリーンショット 2021-11-01 21.02.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645779/9726efba-ec0d-1a94-77fe-783a1901a63c.png)

できてますね！

今回はここまでとします。お疲れ様でした。次回が最終partとなります！