0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-12-31

環境の準備

ターミナルで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)初心者向けチュートリアル翻訳済み

React-Redux-Todo-App

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?