Reactハンズオン[firestoreでTodoアプリ]

## React環境構築

参考：[macで1からReactの環境構築をするのだ](https://qiita.com/spice/items/b75afb607f1d2e1172a2)

- brewインストール
- nodebrewインストール
- パスを通す（bashじゃない人がいたら言ってください）
- nodejsのインストール

ここまで記事通り

### yarnのインストール


```bash
> npm -v
6.9.0
```
npmが入っていれば

```bash
> npm i -g yarn
```

### create-react-appのインストール

```bash
> yarn global add create-react-app
...
...
success Installed "create-react-app@3.1.1" with binaries:
      - create-react-app
✨  Done in 11.25s.
```

### プロジェクトを作成してみる

```bash
> create-react-app my_app
> cd my_app
> yarn start
```

ローカルサーバーが立ち上がってhttp://localhost:3000 にアクセスすれば

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/077274b7-235b-7596-3db7-1f0b4ee2329b.png)

この画面が出てくるはずです

**もうかっこいい**


## React入門

### stateとprops
Reactを始めるのに欠かせないstateとpropsについて

#### state
コンポーネントが持つ状態（変数）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/85a48d89-c95a-42c0-14fc-ef751d4d1925.png)


#### props
コンポーネント間で共有される変数
他のコンポーネントのstateをpropsとして受け取ることもできるし
他のコンポーネントにstateをpropsとして渡すこともできます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/9eef148b-622a-e455-1e0c-bc7fc5b3e521.png)



### 書き換えながら理解しましょう

**今回はReact16.8で導入されたHooksを使用して書いていきます
これまで使用されてきた書き方との比較はhttps://ja.reactjs.org/docs/hooks-state.html を参照してください**

`App.jsx` を以下のように変更します

```react:App.jsx
import React, { useState } from 'react';
import './App.css';
import Todo from './pages/Todo';

function App() {
  // 第１変数がstate, 第２変数がstateを変化させる関数
  const [input, setInput] = useState('');
  const [todoList, setTodoList] = useState([]);

  const addTodo = () => {
    setTodoList([...todoList, input]);
    setInput('');
  }

  const deleteTodo = (index) => {
    setTodoList(todoList.filter((_, idx) => idx !== index))
  }

  return (
    <div className="App">
      <input onChange={(e) => setInput(e.target.value)} value={input}/>
      <button onClick={() => addTodo()}>追加</button>
      {/* todoListという変数とdeleteTodoという関数をpropsとしてTodoコンポーネントに渡している*/}
      <Todo todoList={todoList} deleteTodo={deleteTodo}/>
    </div>
  );
}

export default App;

```

```bash
> cd src
> mkdir pages
> cd pages
> mkdir Todo
> cd Todo
> touch index.jsx
```

で`src/pages/Todo`配下に`index.jsx`を作成してください

`src/pages/Todo/index.jsx`を以下のように書き換えます


```react:index.jsx
import React from 'react';

// todoListという変数とdeleteTodoという関数をpropsとして受け取る
const Todo = ({todoList, deleteTodo}) => (
  <div>
    {/*受け取ったtodoListを使って表示する*/}
    {todoList.map((todo, idx) => (
      <div>
        {todo}
        <button onClick={() => deleteTodo(idx)}>削除</button>
      </div>
    ))}
  </div>
)

export default Todo;
```

ここまで書けばこんな感じ（↓）になるはずです

![Screen Recording 2019-08-31 at 21.48.29.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/36cdab3f-162e-4f37-9f5a-f812a4f809f3.gif)

**stateとprops若干わかりました？？**

### ちょっと寄り道
#### styled-component
htmlのタグのスタイルをカスタムした状態でコンポーネントを定義できます
下は`<p>`タグを`<Title>`という名前で定義してスタイルを当てている例

```react
const Title = styled.p`
  font-size: 26px;
  color: #0097a7;
  letter-spacing: 2.8px;
  font-weight: 200;
`;
```

```bash
> yarn add styled-components
```

### コンポーネントを有効活用してみましょう

先ほど作成したTodoコンポーネントを利用して完了済みTodoリストも同時に表示してみましょう
さらに完了済みボタンと戻すボタンも追加して、Todoの状態を変更できるようにしてみましょう

`App.jsx`を以下のように書き換える

```react:App.jsx
import React, { useState } from 'react';
import './App.css';
import Todo from './pages/Todo';
import styled from 'styled-components'

function App() {

  // 第１変数がstate, 第２変数がstateを変化させる関数
  const [input, setInput] = useState('');
  const [todoList, setTodoList] = useState([]);
  const [finishedList, setFinishedList] = useState([]);

  const addTodo = () => {
    if (!!input) {
      setTodoList([...todoList, input]);
      setInput('');
    }
  }

  const deleteTodo = (index) => {
    setTodoList(todoList.filter((_, idx) => idx !== index))
  }

  const deleteFinishTodo = (index) => {
    setFinishedList(finishedList.filter((_, idx) => idx !== index))
  }

  const finishTodo = (index) => {
    deleteTodo(index)
    setFinishedList([...finishedList,todoList.find((_, idx) => idx === index)])
  }

  const reopenTodo = (index) => {
    deleteFinishTodo(index)
    setTodoList([...todoList,finishedList.find((_, idx) => idx === index)])
  }

  return (
    <div className="App">
      <Title>Todoリスト</Title>
      <input onChange={(e) => setInput(e.target.value)} value={input}/>
      <button onClick={() => addTodo()}>追加</button>
      <TodoContainer>
      {/* todoListという変数とdeleteTodoという関数をpropsとしてTodoコンポーネントに渡している*/}
        <SubContainer>
          <SubTitle>未完了</SubTitle>
          <Todo todoList={todoList} deleteTodo={deleteTodo} changeTodoStatus={finishTodo} type="todo"/>
        </SubContainer>
        <SubContainer>
          <SubTitle>完了済み</SubTitle>
          <Todo todoList={finishedList} deleteTodo={deleteFinishTodo} changeTodoStatus={reopenTodo} type="done"/>
        </SubContainer>
      </TodoContainer>
    </div>
  );
}

export default App;

const Title = styled.p`
  font-size: 26px;
  color: #0097a7;
  letter-spacing: 2.8px;
  font-weight: 200;
`;

const SubTitle = styled.p`
  font-size: 22px;
  color: #5c5c5c;
`;

const SubContainer = styled.div`
  width: 400px;
`;

const TodoContainer = styled.div`
  display: flex;
  flex-direction: row;
  width: 80%;
  margin: 0 auto;
  justify-content: space-between;
`;
```

`src/pages/Todo/index.jsx`もちょっと変更

```react:index.jsx
import React from 'react';
import styled from 'styled-components'

// todoListという変数とdeleteTodoという関数をpropsとして受け取る
const Todo = ({todoList, deleteTodo, changeTodoStatus, type}) => (
  <div>
    {/*受け取ったtodoListを使って表示する*/}
    {todoList.map((todo, idx) => (
      <Container>
        {todo}
        <button onClick={() => deleteTodo(idx)}>削除</button>
        <button onClick={() => changeTodoStatus(idx)}>{type === "todo" ? "完了済みにする" : "戻す"}</button>
      </Container>
    ))}
  </div>
)

export default Todo;

const Container = styled.div`
  color: #5c5c5c;
  letter-spacing: 1.8px;
`;
```

ついでに`index.css`も変更

```css:index.css
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background-color: #fff2cc; /*ここ追加*/
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
    monospace;
}
```

ここまで行けば

![Screen Recording 2019-08-31 at 22.26.05.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/91ba0ad5-c368-e999-8240-7fe3e1bcb9b4.gif)

完了済みボタンを押せばTodoが完了済みリストに追加され
戻すボタンを押せばTodoが未完了リストに戻るようになるはずです


### firestoreを使ってデータを永続化してみる
ここまではデータをstateで管理していたがstateはブラウザをリロードすると初期化されてしまいます
そこでデータをdbに保存して管理して上げる必要があります
今回は簡単にデータを保管できるfirebaseのcloud firestoreを使用してTodoのデータを保持してみましょう

### firebaseにプロジェクトを作成してみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/6c64a840-abd0-81e6-c231-20be972ea0c1.png)

プロジェクトを追加で新しいプロジェクトを作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/518d8066-4892-160a-174b-1e93d37dc579.png)

適当に名前をつけて続行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/cc0c2079-c3dc-cc9b-b4bd-0aa7cc1f2500.png)

アナリティクスは設定しないを押せばプロジェクトが作られます

プロジェクトの作成が完了したら

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/fca3acea-a08d-c766-86d8-c7eaa354d2f3.png)

`</>`のマークを押します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/4592defa-25fe-cdcd-205c-c2c748505756.png)

適当にニックネームをつけてHostingは設定しなくていいです（あとでやります）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/cfcb24ad-b170-84b9-777c-3470ab2a26d6.png)

```react
var firebaseConfig = {
...
}
firebase.initializeApp(firebaseConfig);
```
をコピーして次へ進んでください


一旦ターミナルに戻って先ほど作成したプロジェクトの配下で


```bash
> yarn global add firebase-tools
> yarn add firebase
```

コマンドラインでfirebaseにログインします

```bash
> firebase login
```

ログインができたら`src/index.js`の先頭に先ほどコピーしたfirebaseのConfigを貼り付けます

```react:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import firebase from 'firebase';

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "xxxxxxxxxxxxxxxxxxxxxxx",
  authDomain: "xxxxxxxxxxxxxxxxxxxxxxxx",
  databaseURL: "xxxxxxxxxxxxxxxxxxxxxxx",
  projectId: "xxxxxxxxxxxxxx",
  storageBucket: "",
  messagingSenderId: "xxxxxxxxxxxx",
  appId: "xxxxxxxxxxxxxxxxxxxxxx"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

### firestoreでデータベースを作成してみる


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/e18a8439-6e79-82ac-e098-a64f8e8a775a.png)

`Database`を選択し、画面が遷移したら`データベースの作成`を選択します
モーダルが表示されたら`テストモードで開始`を選択して次へ進みます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/54f1dedc-31fc-681a-0aec-10ac80b9d269.png)

ロケーションはどこでもいいので完了を押します

これでfirestoreの設定は終了

画面が移動したらコレクションの開始を選択して`todoList`と入力して次へ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/a6b152b2-d77d-c1f1-90c2-d5fa59b5a8ff.png)

ドキュメントIDとフィールドとタイプを以下のように設定して保存

今回は簡単に文字列の配列で保存します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/bcb296c3-208f-6121-5a9d-50677ca904ff.png)

次にドキュメントの追加を選択してドキュメントIDとフィールドとタイプを設定して保存

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/f00e6918-c7bc-ab5e-d0dc-9a1283eb3336.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/60997916-df80-459b-9b62-d615d4ad002c.png)

こうなればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/8c4f8aad-2d63-7831-c790-97d020ecdabc.png)


### reactからfirestoreを操作してみる

`App.jsx`を以下のように書き換えてみましょう

```react:App.jsx
import React, { useState, useEffect } from 'react'; // 修正
import './App.css';
import Todo from './pages/Todo';
import styled from 'styled-components';
import firebase from 'firebase'; // 追記
import 'firebase/firestore'; // 追記

function App() {

  // 第１変数がstate, 第２変数がstateを変化させる関数
  const [input, setInput] = useState('');
  const [todoList, setTodoList] = useState([]);
  const [finishedList, setFinishedList] = useState([]);
  // Loadingを判定する変数
  const [isLoading, setIsLoading] = useState(true);
  // 未完了のTodoが変化したかを監視する変数
  const [isChangedTodo, setIsChangedTodo] = useState(false);
  // 完了済みのTodoが変化したかを監視する変数
  const [isChangedFinished, setIsChangedFinished] = useState(false);

  const db = firebase.firestore(); // 追記

  // 追記 一番最初にfirestoreからデータを取ってきてstateに入れる
  useEffect(() => {
    (async () => {
      const resTodo = await db.collection("todoList").doc("todo").get();
      // stateに入れる
      setTodoList(resTodo.data().tasks);
      const resFinishedTodo = await db.collection("todoList").doc("finishedTodo").get();
      // stateに入れる
      setFinishedList(resFinishedTodo.data().tasks);
      // Loading終了
      setIsLoading(false);
    })()
  }, [db])

  useEffect(() => {
    if (isChangedTodo) {
      (async () => {
        // 通信をするのでLoadingをtrue
        setIsLoading(true)
        const docRef = await db.collection('todoList').doc('todo');
        docRef.update({ tasks: todoList })
        // Loading終了
        setIsLoading(false)
      })()
    }
  }, [todoList, isChangedTodo, db])

  useEffect(() => {
    if (isChangedFinished) {
      (async () => {
        // 通信をするのでLoadingをtrue
        setIsLoading(true)
        const docRef = await db.collection('todoList').doc('finishedTodo');
        docRef.update({ tasks: finishedList })
        // Loading終了
        setIsLoading(false)
      })()
    }
    setIsChangedFinished(false)
  }, [db, finishedList, isChangedFinished])

  const addTodo = async () => {
    if (!!input) {
      // 追記 Todoが変化したのでtrue
      setIsChangedTodo(true);
      setTodoList([...todoList, input]);
      setInput('');
    }
  }

  const deleteTodo = (index) => {
    // 追記 Todoが変化したのでtrue
    setIsChangedTodo(true);
    setTodoList(todoList.filter((_, idx) => idx !== index))
  }

  const deleteFinishTodo = (index) => {
    // 追記 完了済みTodoが変化したのでtrue
    setIsChangedFinished(true);
    setFinishedList(finishedList.filter((_, idx) => idx !== index))
  }

  const finishTodo = (index) => {
    // 追記 Todo、完了済みTodoがともに変化したのでtrue
    setIsChangedTodo(true);
    setIsChangedFinished(true);
    deleteTodo(index)
    setFinishedList([...finishedList,todoList.find((_, idx) => idx === index)])
  }

  const reopenTodo = (index) => {
    // 追記 Todo、完了済みTodoがともに変化したのでtrue
    setIsChangedTodo(true);
    setIsChangedFinished(true);
    deleteFinishTodo(index)
    setTodoList([...todoList,finishedList.find((_, idx) => idx === index)])
  }

  return (
    <div className="App">
      <Title>Todoリスト</Title>
      <input onChange={(e) => setInput(e.target.value)} value={input}/>
      <button onClick={() => addTodo()}>追加</button>
      {isLoading ? 
        <Loading>loading</Loading>
      :
        <TodoContainer>
        {/* todoListという変数とdeleteTodoという関数をpropsとしてTodoコンポーネントに渡している*/}
          <SubContainer>
            <SubTitle>未完了</SubTitle>
            <Todo todoList={todoList} deleteTodo={deleteTodo} changeTodoStatus={finishTodo} type="todo"/>
          </SubContainer>
          <SubContainer>
            <SubTitle>完了済み</SubTitle>
            <Todo todoList={finishedList} deleteTodo={deleteFinishTodo} changeTodoStatus={reopenTodo} type="done"/>
          </SubContainer>
        </TodoContainer>
      }
    </div>
  );
};

export default App;

const Title = styled.p`
  font-size: 26px;
  color: #0097a7;
  letter-spacing: 2.8px;
  font-weight: 200;
`;

const SubTitle = styled.p`
  font-size: 22px;
  color: #5c5c5c;
`;

const SubContainer = styled.div`
  width: 400px;
`;

const TodoContainer = styled.div`
  display: flex;
  flex-direction: row;
  width: 80%;
  margin: 0 auto;
  justify-content: space-between;
`;

const Loading = styled.div`
  margin: 40px auto;
`;
```

これでリロードしてもデータが消えなくなりました😇
やったね😂

![Screen Recording 2019-09-01 at 0.39.23.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/3660304b-287e-b76d-6f5e-0d76535c122d.gif)


### firebaseにデプロイしてみる

今のままではローカル環境でしか動かないのでfirebaseにホスティングさせて外部からも見れるようにしてみましょう

プロジェクトの配下で操作してください

```bash
> firebase init
```

`hosting`を選択してスペースを押す→Enter

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/fc8dad3e-3804-6e49-68d1-fd52fe37f0bd.png)

`Use an exsisting Project`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/261deebd-9336-9ee3-1e9a-2bfaf15d089c.png)

先ほど作成したプロジェクトを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/7c05a3d2-b5d7-823a-14ed-0a4c6227a5fa.png)

`What do you want to use as your public directory?` → `build`と入力してEnter
`Configure as a single-page app (rewrite all urls to /index.html)?` →`y`と入力してEnter

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/faabd639-913c-6da3-2fa1-6f10e7b4559c.png)

こうなればOK

あとはビルドしてデプロイするだけ！

```bash
> yarn build
> firebase deploy
```

URLが表示されるのでアクセスすれば完成！！
お疲れさまでした
https://todolist-bc9ed.firebaseapp.com/

firebaseのログイン機能を使用してユーザー登録をすればユーザー毎のTodoを保存できるようになるのでぜひ挑戦してみてください〜！

## 質問＆カスタマイズタイム

### おまけ
Material UIを使ってちょっとマシなデザインにしてみよう

```bash
> yarn add @material-ui/core
> yarn add @material-ui/icons
```

```react:App.js
import React, { useState, useEffect} from 'react'; // 修正
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import './App.css';
import Todo from './pages/Todo';
import styled from 'styled-components';
import firebase from 'firebase'; // 追記
import 'firebase/firestore'; // 追記

function App() {

  // 第１変数がstate, 第２変数がstateを変化させる関数
  const [input, setInput] = useState('');
  const [todoList, setTodoList] = useState([]);
  const [finishedList, setFinishedList] = useState([]);
  // Loadingを判定する変数
  const [isLoading, setIsLoading] = useState(true);
  // 未完了のTodoが変化したかを監視する変数
  const [isChangedTodo, setIsChangedTodo] = useState(false);
  // 完了済みのTodoが変化したかを監視する変数
  const [isChangedFinished, setIsChangedFinished] = useState(false);

  const db = firebase.firestore(); // 追記

  // 追記 一番最初にfirestoreからデータを取ってきてstateに入れる
  useEffect(() => {
    (async () => {
      const resTodo = await db.collection("todoList").doc("todo").get();
      // stateに入れる
      setTodoList(resTodo.data().tasks);
      const resFinishedTodo = await db.collection("todoList").doc("finishedTodo").get();
      // stateに入れる
      setFinishedList(resFinishedTodo.data().tasks);
      // Loading終了
      setIsLoading(false);
    })()
  }, [db])

  useEffect(() => {
    if (isChangedTodo) {
      (async () => {
        // 通信をするのでLoadingをtrue
        setIsLoading(true)
        const docRef = await db.collection('todoList').doc('todo');
        docRef.update({ tasks: todoList })
        // Loading終了
        setIsLoading(false)
      })()
    }
  }, [todoList, isChangedTodo, db])

  useEffect(() => {
    if (isChangedFinished) {
      (async () => {
        // 通信をするのでLoadingをtrue
        setIsLoading(true)
        const docRef = await db.collection('todoList').doc('finishedTodo');
        docRef.update({ tasks: finishedList })
        // Loading終了
        setIsLoading(false)
      })()
    }
    setIsChangedFinished(false)
  }, [db, finishedList, isChangedFinished])

  const addTodo = async () => {
    if (!!input) {
      // 追記 Todoが変化したのでtrue
      setIsChangedTodo(true);
      setTodoList([...todoList, input]);
      setInput('');
    }
  }

  const deleteTodo = (index) => {
    // 追記 Todoが変化したのでtrue
    setIsChangedTodo(true);
    setTodoList(todoList.filter((_, idx) => idx !== index))
  }

  const deleteFinishTodo = (index) => {
    // 追記 完了済みTodoが変化したのでtrue
    setIsChangedFinished(true);
    setFinishedList(finishedList.filter((_, idx) => idx !== index))
  }

  const finishTodo = (index) => {
    // 追記 Todo、完了済みTodoがともに変化したのでtrue
    setIsChangedTodo(true);
    setIsChangedFinished(true);
    deleteTodo(index)
    setFinishedList([...finishedList,todoList.find((_, idx) => idx === index)])
  }

  const reopenTodo = (index) => {
    // 追記 Todo、完了済みTodoがともに変化したのでtrue
    setIsChangedTodo(true);
    setIsChangedFinished(true);
    deleteFinishTodo(index)
    setTodoList([...todoList,finishedList.find((_, idx) => idx === index)])
  }

  return (
    <div className="App">
      <Title>Todoリスト</Title>
      <TextField onChange={(e) => setInput(e.target.value)} value={input}/>
      <Button variant="contained" color="primary" onClick={() => addTodo()}>追加</Button>
      {isLoading ? 
        <Loading>loading</Loading>
      :
        <TodoContainer>
        {/* todoListという変数とdeleteTodoという関数をpropsとしてTodoコンポーネントに渡している*/}
          <SubContainer>
            <SubTitle>未完了</SubTitle>
            <Todo todoList={todoList} deleteTodo={deleteTodo} changeTodoStatus={finishTodo} type="todo"/>
          </SubContainer>
          <SubContainer>
            <SubTitle>完了済み</SubTitle>
            <Todo todoList={finishedList} deleteTodo={deleteFinishTodo} changeTodoStatus={reopenTodo} type="done"/>
          </SubContainer>
        </TodoContainer>
      }
    </div>
  );
};

export default App;

const Title = styled.p`
  font-size: 26px;
  color: #0097a7;
  letter-spacing: 2.8px;
  font-weight: 200;
`;

const SubTitle = styled.p`
  font-size: 22px;
  color: #5c5c5c;
`;

const SubContainer = styled.div`
  width: 400px;
`;

const TodoContainer = styled.div`
  display: flex;
  flex-direction: row;
  width: 80%;
  margin: 0 auto;
  justify-content: space-between;
`;

const Loading = styled.div`
  margin: 40px auto;
`;
```

`Todo/index.jsx`

```react:index.jsx
import React from 'react';
import styled from 'styled-components';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';

// todoListという変数とdeleteTodoという関数をpropsとして受け取る
const Todo = ({todoList, deleteTodo, changeTodoStatus, type}) => (
  <div>
    {/*受け取ったtodoListを使って表示する*/}
    {todoList.map((todo, idx) => (
      <Container key={todo}>
        {todo}
        <IconButton aria-label="delete">
          <DeleteIcon fontSize="small" onClick={() => deleteTodo(idx)} />
        </IconButton>
        <Button variant="outlined" color={type === "todo" ? "primary" : "secondary"} onClick={() => changeTodoStatus(idx)}>{type === "todo" ? "完了済みにする" : "戻す"}</Button>
      </Container>
    ))}
  </div>
);

export default Todo;

const Container = styled.div`
  color: #5c5c5c;
  letter-spacing: 1.8px;
`;
```

ちょっとマシになったよね？笑

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/6d85d956-96c3-2474-2030-7ba047ecfd8d.png)

ちょっと頑張ればこんな感じにも（まだダサいのは許して）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/90661545-405d-aa88-4b83-b1fa715f52a7.gif" width=270 />

![output-palette-none2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192773/6b01fc3d-7247-91d7-6832-0c41a4bc126a.gif)

完成品
https://todolist-bc9ed.web.app/

ソースはここにおいておきます
ログイン機能とか作りたくてもわからんって人は気軽に言ってね😊
https://github.com/mr04vv/ReactTodoHandsOn/tree/custom