React （redux-toolkit, firebasev9 ）の構成でアプリを作成しました【5】【Keep Clone】

## 環境の準備

#### ターミナルでreactアプリケーションを作成する。

```
$ npx create-react-app <プロジェクト名>
% cd <プロジェクト名>
% npm start
```

#### 必要な環境をインストール

公式サイト：[Redux Toolkit]
(https://redux-toolkit.js.org/)

```
$ npm install --save @reduxjs/toolkit react-redux
$ npm install react-icons --save
$ npm install react-router-dom
$ npm install --save react-modal
$ npm install react-bootstrap bootstrap@5.1.3
```


コンポーネント・ファイル構成

```
src
 ├── components
    ├── DisplayTodos.js
    ├── TodoItem.js
    └──  Todos.js
├── css
    └── main.css
├── redux
    ├── reducer.js
    └── store.js
 ├── App.js
 ├── index.js
```

#### ベースとなる画面構成を作成する

```
$ mkdir src/components
$ mkdir src/css
$ mkdir src/redux
$ touch src/components/DisplayTodos.js
$ touch src/components/TodoItem.js
$ touch src/components/Todos.js
$ touch src/css/main.css
$ touch src/redux/store.js
$ touch src/redux/reducer.js
```

## Todoリスト作成

#### App.jsを編集する

```App.js

import "./css/main.css";
import DisplayTodos from "./components/DisplayTodos";
import Todos from "./components/Todos";

function App() {
  return (
    <div className="App">
      <h1>タイトル</h1>
      <Todos />
      <DisplayTodos />
    </div>
  );
}

export default App;
```
#### index.jsを編集する

```index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import store from "./redux/store";

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);
```

## src/components/DisplayTodos.js Todos.js TodoItem.jsを編集

```src/components/DisplayTodos.js
import React, { useState } from "react";
import { connect } from "react-redux";
import {
  addTodos,
  completeTodos,
  removeTodos,
  updateTodos,
} from "../redux/reducer";
import TodoItem from "./TodoItem";

const mapStateToProps = (state) => {
  return {
    todos: state,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    addTodo: (obj) => dispatch(addTodos(obj)),
    removeTodo: (id) => dispatch(removeTodos(id)),
    updateTodo: (obj) => dispatch(updateTodos(obj)),
    completeTodo: (id) => dispatch(completeTodos(id)),
  };
};

const DisplayTodos = (props) => {
  const [sort, setSort] = useState("active");
  return (
    <div className="displaytodos">
      <div className="buttons">
        <button onClick={() => setSort("active")}>Active</button>
        <button onClick={() => setSort("completed")}>Completed</button>
        <button onClick={() => setSort("all")}>All</button>
      </div>
      <ul>
        {props.todos.length > 0 && sort === "active"
          ? props.todos.map((item) => {
              return (
                item.completed === false && (
                  <TodoItem
                    key={item.id}
                    item={item}
                    removeTodo={props.removeTodo}
                    updateTodo={props.updateTodo}
                    completeTodo={props.completeTodo}
                  />
                )
              );
            })
          : null}
        {props.todos.length > 0 && sort === "completed"
          ? props.todos.map((item) => {
              return (
                item.completed === true && (
                  <TodoItem
                    key={item.id}
                    item={item}
                    removeTodo={props.removeTodo}
                    updateTodo={props.updateTodo}
                    completeTodo={props.completeTodo}
                  />
                )
              );
            })
          : null}
        {props.todos.length > 0 && sort === "all"
          ? props.todos.map((item) => {
              return (
                <TodoItem
                  key={item.id}
                  item={item}
                  removeTodo={props.removeTodo}
                  updateTodo={props.updateTodo}
                  completeTodo={props.completeTodo}
                />
              );
            })
          : null}
      </ul>
    </div>
  );
};

export default connect(mapStateToProps, mapDispatchToProps)(DisplayTodos);
```

```src/components/Todos.js
import React, { useState } from "react";
import { connect } from "react-redux";
import { addTodos } from "../redux/reducer";
import { Button, Navbar } from "react-bootstrap";
import { GoPlus } from "react-icons/go";

const mapStateToProps = (state) => {
  return {
    todos: state,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    addTodo: (obj) => dispatch(addTodos(obj)),
  };
};

const Todos = (props) => {
  const [todo, setTodo] = useState("");

  const handleChange = (e) => {
    setTodo(e.target.value);
  };

  const add = () => {
    if (todo === "") {
      alert("Input is Empty");
    } else {
      props.addTodo({
        id: Math.floor(Math.random() * 1000),
        item: todo,
        completed: false,
      });
      setTodo("");
    }
  };

  return (
    <>
      <div className="Navbar">
        <Navbar bg="green" variant="green">
          <Navbar.Brand> login's Dogaben</Navbar.Brand>
          <Button type="button" className="btn btn-light">
            Logout
          </Button>
        </Navbar>
      </div>
      <div className="addTodos">
        <input
          type="text"
          onChange={(e) => handleChange(e)}
          className="todo-input"
          value={todo}
        />
        <button className="add-btn" onClick={() => add()}>
          <GoPlus />
        </button>
        <br />
      </div>
    </>
  );
};
export default connect(mapStateToProps, mapDispatchToProps)(Todos);
```

```src/components/TodoItem.js
import React, { useRef } from "react";

const TodoItem = (props) => {
  const { item, updateTodo, removeTodo, completeTodo } = props;

  const inputRef = useRef(true);

  const changeFocus = () => {
    inputRef.current.disabled = false;
    inputRef.current.foucus();
  };

  const update = (id, value, e) => {
    if (e.which === 13) {
      updateTodo({ id, item: value });
      inputRef.current.disabled = true;
    }
  };
  return (
    <li key={item.id} className="card">
      <textarea
        ref={inputRef}
        disabled={inputRef}
        defaultValue={item.item}
        onKeyPress={(e) => update(item.id, inputRef.current.value, e)}
      />
      <div className="btns">
        <button onClick={() => changeFocus()}>Edit</button>
        <button onClick={() => completeTodo(item.id)}>Complete</button>
        <button onClick={() => removeTodo(item.id)}>Delete</button>
        {""}
      </div>
      {item.completed && <span className="completed">done</span>}
    </li>
  );
};

export default TodoItem;
```

## src/redux/reducer.js store.jsを編集


#### src/redux/reducer.jsを編集する。

```src/redux/reducer.js
import { createSlice } from "@reduxjs/toolkit";

const initialState = [];

const addTodoReducer = createSlice({
  name: "todos",
  initialState,
  reducers: {
    addTodos: (state, action) => {
      state.push(action.payload);
      return state;
    },
    removeTodos: (state, action) => {
      return state.filter((item) => item.id !== action.payload);
    },
    updateTodos: (state, action) => {
      return state.map((todo) => {
        if (todo.id === action.payload.id) {
          return {
            ...todo,
            item: action.payload.item,
          };
        }
        return todo;
      });
    },
    completeTodos: (state, action) => {
      return state.map((todo) => {
        if (todo.id === action.payload) {
          return {
            ...todo,
            completed: true,
          };
        }
        return todo;
      });
    },
  },
});

export const { addTodos, removeTodos, updateTodos, completeTodos } =
  addTodoReducer.actions;
export const reducer = addTodoReducer.reducer;   
```

#### src/redux/store.jsを編集する。

```src/redux/store.js
import { configureStore } from "@reduxjs/toolkit";
import { reducer } from "./reducer";

const store = configureStore({
  reducer: reducer,
});
export default store;
```

#### src/css/main.cssを編集

```src/css/main.css
html {
  line-height: 1.15;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  background: linear-gradient(190deg, rgb(55, 216, 68) 0%, rgb(4, 94, 34) 100%);
  background-repeat: no-repeat;
  background-size: cover;
  background-attachment: fixed;
  color: white;
  overflow: hidden;
}

.App {
  margin-top: 0rem;
  display: flex;
  flex-direction: column;
}

.App h1 {
  margin-top: 0rem;
  display: inline;
  /* text-align: flex;
  margin-top: 2rem; */
}

.addTodos {
  margin-top: 2rem;
  display: flex;
  justify-content: center;
}

.todo-input {
  min-width: 25rem;
  width: 30vw;

  max-height: 2.5rem;
  background-color: white;
  border: none;
  border-radius: 5px;
  padding: 0.5rem 1rem;

  align-self: center;
}

.todo-input:focus {
  outline: none;
  border: 2px solid darkgreen;
}

.add-btn {
  margin-left: 1rem;
  background-color: darkgreen;
  color: white;
  border-radius: 50%;
  border: 2.5px solid white;
  font-size: 1.5rem;
  width: 3.2rem;
  height: 3.2rem;
  cursor: pointer;
  box-shadow: 2px 4px 16px darkgreen;

  display: flex;
  justify-content: center;
  align-items: center;
}

.add-btn:focus {
  outline: none;
}

.displaytodos .buttons {
  margin-bottom: 2rem;
}

.displaytodos .buttons button {
  padding: 0.5rem 1.2rem;
  border-radius: 5px;
  cursor: pointer;
  border: none;
  background-color: darkgreen;
  color: white;
  border: 2px solid white;
}

/* .displaytodos .buttons button :focus {
  outline: none;
}

.displaytodos .buttons button :not(:last-child) {
  margin-right: 1rem;
} */

.displaytodos ul {
  list-style: none;
  display: flex;
  align-self: flex-start;
  flex-wrap: wrap;
  margin-left: 0%;
}

.card {
  display: flex;
  flex-direction: column;
  background: white;
  background: radial-gradient(circle, white, white 100%);

  margin: 0 2rem 1rem 0;
  height: 8rem;
  width: 16rem;
  border-radius: 0.2rem;
  padding: 0rem;

  position: relative;
}

.card textarea {
  padding: 0.5rem;
  border-radius: 8px;
  border: none;
  background-color: #e1ebfd;

  color: #271c6c;
  height: 70%;
}
.card textarea:disabled {
  background-color: transparent;
}

.card .btns {
  position: absolute;
  bottom: 0rem;
  right: 0rem;
}

.card .btns button {
  padding: 0.2rem 1.2rem;
  border-radius: 5px;
  cursor: pointer;
  border: none;
  background-color: white;
  color: darkgreen;
  border: 3px solid darkgreen;
  /* background-color: darkgreen;
  color: white;
  border: 2px solid white; */
}

.card .btns button:focus {
  outline: none;
}
```



## 参考サイト
[ReduxでTodoリストアプリを構築する😍| （React-Redux）初心者向けチュートリアル翻訳済み](https://www.youtube.com/watch?v=YhgSuUkWlK4)

[React-Redux-Todo-App](https://github.com/codebucks27/React-Redux-Todo-App)