0
1

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でアプリを作成しました【8】【Firebaseを使ったTodoリスト】

Last updated at Posted at 2021-11-20

##Reactアプリの作成

% npm init react-app  <アプリ名>

パッケージをインストールする

% npm install

##Firebaseへデプロイ

#####① Firebase『使ってみる』をクリック

#####②『プロジェクトを作成』をクリックし、プロジェクト名を入力

#####③ googleアナリティクスを無効にして、『プロジェクトを作成』をクリック

#####④『続行』をクリックし、プロジェクトの概要『Hositing』を選択し、『始める』をクリック

#####⑤ Firebase CLIをインストール

  • firebase -Vで、firebaseがインストールされているか確認する
$ npm install -g firebase-tools

#####⑥『次へ』をクリックし、プロジェクとの初期化『Google へのログイン』をインストール

$ firebase login

#####⑦『Firebase CLI が Google アカウントへのアクセスをリクエストしています』の表示が出るので、『許可』をクリック

#####⑧アプリケーションホルダーへ移動し、ルート ディレクトリから実行する

$ cd  <プロジェクト名>
$ firebase init

#####⑨ スペースキーで、下記を選択する

 Hosting: Configure files
 for Firebase Hosting and (optionally) set up GitHub Action deploys
Use an existing projectで、作成したプロジェクト名を選択する
buildを選択し、『No』を選択する

#####⑩FirebaseへデプロイするとHositing URL:が発行される

$ firebase deploy
$ npm run build

##Todoリスト作成
##Bootstrapのインストール

#####Adding Bootstrapのコマンドをインストールする

$ npm install --save bootstrap

$ npm install --save reactstrap react react-dom
App.js
import 'bootstrap/dist/css/bootstrap.min.css';

##Formの作成 / App.jsを編集する

App.js
import React, {useState} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import { Container, Form, InputGroup, Input, InputGroupAddon, Button } from 'reactstrap';

function App() {
  const [value, setValue] = useState('')
  const [todos, setTodos] = useState([])

  const handleSubmit = e => {
    e.preventDefault()
    addTodo(value)
  }

  const addTodo = text => {
    const newTodos = [...todos, text]
    setTodos(newTodos)
  }
  return (
    <div className="App">
      <Container>
        <h1 className="mt-4">Todoリスト</h1>
        <Form onSubmit={handleSubmit}>
          <InputGroup>
            <Input type="text"
              value={value}
              onChange={e => setValue(e.target.value)} />
            <InputGroupAddon addonType="append">
              <Button type="submit" color="primary">追加</Button>
            </InputGroupAddon>
            </InputGroup>
        </Form>
     </Container>
    </div>
  );
}
export default App;

##リスト表示 + 削除ボタンの追加/完了・未完了変更処理の追加

App.js
import React, {useState} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import { Container, Form, InputGroup, Input, InputGroupAddon, Button } from 'reactstrap';

function App() {
  const [value, setValue] = useState('')
  const [todos, setTodos] = useState([])

  const handleSubmit = e => {
    e.preventDefault()
    addTodo(value)
    setValue('')
  }

  const addTodo = text => {
    //addTodoでデータを保存する際に、オブジェクトにして completeというstatesを加える
    const newTodos = [...todos, { text, complete: false }]
    setTodos(newTodos)

  const removeTodo = index => {
    const newTodos = [...todos]
    //要素の削除には splice を使用する spliceは、配列のindex指定し、第二引数の渡した個数分を第一引数のindexを削除する  
    //第一引数 indexを指定し 第二引数 1 を入力する
    newTodos.splice(index, 1)
   // 指定した要素を抜き取った新しい配列を再度、setTodosで指定する
    setTodos(newTodos)
  }
    
    const completeTodo = index => {
      //completeTodoの中では、statesがtrueの時はfalse  falseの時はtrue に変更するようにする
      const newTodos = [...todos]
      //引数として指定したindexを使用して、特定のtodoを指定する
      newTodos[index].complete = !newTodos[index].complete
      //更新されたTodoリストをセットして完了
      setTodos(newTodos)
      //違いがわかるようにconsole.logを記載
  }  
  return (
    <div className="App">
      <Container>
        <h1 className="mt-4">Todoリスト</h1>
        <Form onSubmit={handleSubmit}>
          <InputGroup>
            <Input type="text"
              value={value}
              onChange={e => setValue(e.target.value)} />
            <InputGroupAddon addonType="append">
              <Button type="submit" color="primary">追加</Button>
            </InputGroupAddon>
            </InputGroup>
        </Form>
     </Container>
      <Container>
        {/* tableタグを使ってリストを表示させる  */}
        <Table>
          <tbody>
            {/* 論理演算子を使い、処理がある時のみ実行する */}
            {todos && todos.map((todo, index) => (
              // keyにindexを与える
            <tr key={index}>
                <th className="text-left" style={{textDecoration: todo.complete ? "line-through" :""}}>
                  {todo.text}
                </th>
                {/* 親要素 textを右に寄せる */}
                <td className="text-right">
                  {/* //完了ボタンを作成  onClickイベントを追加して、completeを実行する */}
                  {/* //statesの変更がわかるようにbuttonのtext/colorを変更する */}
                  <Button
                    {/* jsx内で三項演算子を使いtextを変更する */ }
                    color={todo.complete ? "secondary" : "success"}
                    className="mr-2"
                    onClick={() => completeTodo(index)}>
                    {todo.complete ? "完了" : "未完了"}
                  </Button>
                  {/* //削除ボタンを作成  onClickイベントを追加して、removeを実行する remove関数内にindexを使い 配列から要素を削除する*/}
                  <Button
                    color="danger"
                    onClick={() => removeTodo(index)}>
                    削除
                  </Button>
                </td>
            </tr>
              ))}
          </tbody>
        </Table>
     </Container>
    </div>
  );
}

export default App;

##Firebaseへログイン
#####状況を確認する

$ firebase login

#####react applicationをbuildする

% npm run build

#####Firebaseへアップロードする

% firebase deploy

##Todoアプリをコンポーネント化
#####components ディレクトリを src ディレクトリ配下に配置する
#####TodoForm TodoListコンポーネントを定義し、ファイルを用意する

$ mkdir src/components
$ touch src/components/TodoForm.js
$ touch src/components/TodoList.js
App.js
import React, {useState} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import Container from 'reactstrap';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';

function App() {
  // const [value, setValue] = useState('')
  const [todos, setTodos] = useState([])

   {/* //Form のソースコードを TodoFormへ移行する */}
  // const handleSubmit = e => {
  //   e.preventDefault()
  //   addTodo(value)
  //   setValue('')
  // }

  const addTodo = text => {
    //addTodoでデータを保存する際に、オブジェクトにして completeというstatesを加える
    const newTodos = [...todos, { text, complete: false }]
    setTodos(newTodos)

  const removeTodo = index => {
    const newTodos = [...todos]
    //要素の削除には splice を使用する spliceは、配列のindex指定し、第二引数の渡した個数分を第一引数のindexを削除する  
    //第一引数 indexを指定し 第二引数 1 を入力する
    newTodos.splice(index, 1)
   // 指定した要素を抜き取った新しい配列を再度、setTodosで指定する
    setTodos(newTodos)
  }
    
    const completeTodo = index => {
      //completeTodoの中では、statesがtrueの時はfalse  falseの時はtrue に変更するようにする
      const newTodos = [...todos]
      //引数として指定したindexを使用して、特定のtodoを指定する
      newTodos[index].complete = !newTodos[index].complete
      //更新されたTodoリストをセットして完了
      setTodos(newTodos)
      //違いがわかるようにconsole.logを記載
  }  
  return (
    <div className="App">
      <Container>
        <h1 className="mt-4">Todoリスト</h1>
        {/* //TodoFormのコンポーネント化を行う */}
        <TodoForm addTodo={addTodo}/>
        {/* //TodoListコンポーネントで使用している todos, completeTodo, removeTodo を渡す */}
        <TodoList
          todo={todos}
          removeTodo={removeTodo}
          completeTodo={completeTodo}
        />
     </Container>
    </div>
  );
}
export default App;
TodoList.js
import React from "react";
import { Button, Table } from "reactstrap";
//List内で使用している todos State   complete関数とremove関数をpropsで受け取る
function TodoList({todos, completeTodo, removeTodo}) {
    return (
    <Table>
       <tbody>
            {/* 論理演算子を使い、処理がある時のみ実行する */}
            {todos && todos.map((todo, index) => (
              // keyにindexを与える
            <tr key={index}>
                <th className="text-left" style={{textDecoration: todo.complete ? "line-through" :""}}>
                  {todo.text}
                </th>
                {/* 親要素 textを右に寄せる */}
                <td className="text-right">
                  {/* //完了ボタンを作成  onClickイベントを追加して、completeを実行する */}
                  {/* //statesの変更がわかるようにbuttonのtext/colorを変更する */}
                  <Button
                            {/* jsx内で三項演算子を使いtextを変更する */ }
                    color={todo.complete ? "secondary" : "success"}
                    className="mr-2"
                    onClick={() => completeTodo(index)}>
                    {todo.complete ? "完了" : "未完了"}
                  </Button>
                  {/* //削除ボタンを作成  onClickイベントを追加して、removeを実行する remove関数内にindexを使い 配列から要素を削除する*/}
                  <Button
                    color="danger"
                    onClick={() => removeTodo(index)}>
                    削除
                  </Button>
                </td>
            </tr>
              ))}
          </tbody>
        </Table>
    )
}
export default TodoList;
TodoForm.js
import React, { useState } from "react";
import { Form, InputGroup, Input, InputGroupAddon, Button } from "reactstrap";

//AddTodo関数をpropsとして受け取れるようにする
function TodoForm({ addTodo }) {
  const [value, setValue] = useState("");
  const handleSubmit = (e) => {
    e.preventDefault();
    addTodo(value);
    setValue("");
  };
  return (
    <Form onSubmit={handleSubmit}>
      <InputGroup>
        <Input
          type="text"
          value={value}
          onChange={(e) => setValue(e.target.value)}
        />
        <InputGroupAddon addonType="append">
          <Button type="submit" color="primary">
            追加
          </Button>
        </InputGroupAddon>
      </InputGroup>
    </Form>
  );
}
export default TodoForm;

##ContextAPIを使ったグローバルステート管理

image.png

#####contexパターンの実装方法

#####contexts ディレクトリを src ディレクトリ配下に配置する
#####TodosContextsを定義し、ファイルを用意する

$ mkdir src/contexts
$ touch src/contexts/TodosContext.js
App.js
import React from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import Container from "reactstrap";
import TodoForm from "./components/TodoForm";
import TodoList from "./components/TodoList";

function App() {
return (
    <div className="App">
      <Container>
        <h1 className="mt-4">Todoリスト</h1>
        <TodosContextProvider>
          {/* //TodoFormのコンポーネント化を行う */}
          <TodoForm />
          {/* //TodoListコンポーネントで使用している todos, completeTodo, removeTodo を渡す */}
          <TodoList />
        </TodosContextProvider>
      </Container>
    </div>
  );
}
export default App;
TodoList.js
import React, { useContext } from "react";
import { TodosContext } from "../contexts/TodosContext";
import { Button, Table } from "reactstrap";

function TodoList() {
  //useContextを使って関数とstateをTodoContextから呼び出す
  const { todos, completeTodo, removeTodo } = useContext(TodosContext);
  return (
    <Table>
      <tbody>
        {/* 論理演算子を使い、処理がある時のみ実行する */}
        {todos &&
          todos.map((todo, index) => (
            // keyにindexを与える
            <tr key={index}>
              <th
                className="text-left"
                style={{ textDecoration: todo.complete ? "line-through" : "" }}
              >
                {todo.text}
              </th>
              {/* 親要素 textを右に寄せる */}
              <td className="text-right">
                {/* //完了ボタンを作成  onClickイベントを追加して、completeを実行する */}
                {/* //statesの変更がわかるようにbuttonのtext/colorを変更する */}
                <Button
                  color={todo.complete ? "secondary" : "success"}
                  className="mr-2"
                  onClick={() => completeTodo(index)}
                >
                  {todo.complete ? "完了" : "未完了"}
                </Button>
                {/* //削除ボタンを作成  onClickイベントを追加して、removeを実行する remove関数内にindexを使い 配列から要素を削除する*/}
                <Button color="danger" onClick={() => removeTodo(index)}>
                  削除
                </Button>
              </td>
            </tr>
          ))}
      </tbody>
    </Table>
  );
}
export default TodoList;
TodosContext.js

import React, { createContext, useState } from "react";

export const TodosContext = createContext();
const TodoContextProvider = (props) => {
  const [todos, setTodos] = useState([])
  const addTodo = text => {
    //addTodoでデータを保存する際に、オブジェクトにして completeというstatesを加える
    const newTodos = [...todos, { text, complete: false }]
      setTodos(newTodos)
      console.log(newTodos)

  const removeTodo = index => {
    const newTodos = [...todos]
    //要素の削除には splice を使用する spliceは、配列のindex指定し、第二引数の渡した個数分を第一引数のindexを削除する  
    //第一引数 indexを指定し 第二引数 1 を入力する
    newTodos.splice(index, 1)
   // 指定した要素を抜き取った新しい配列を再度、setTodosで指定する
    setTodos(newTodos)
  }
    const completeTodo = index => {
      //completeTodoの中では、statesがtrueの時はfalse  falseの時はtrue に変更するようにする
      const newTodos = [...todos]
      //引数として指定したindexを使用して、特定のtodoを指定する
      newTodos[index].complete = !newTodos[index].complete
      //更新されたTodoリストをセットして完了
      setTodos(newTodos)
      //違いがわかるようにconsole.logを記載
      console.log(newTodos)
  }  
      return (
          <TodoContext.Provider value={{ todos, addTodo, removeTodo, completeTodo }}>
              {props.children}
          </TodoContext.Provider>
      );
};
export default TodoContextProvider;

TodoForm.js

import React, { useState, useContext } from "react";
import { Form, InputGroup, Input, InputGroupAddon, Button } from "reactstrap";
import { TodosContext } from "../contexts/TodosContext";

function TodoForm() {
  //上記のuseContextの引数にContextオブジェクトを渡すことで、value値の値を使うことができる
  //addTodo関数を受け取る準備をし、useContextに渡すContextオブジェクトをインポートする
  const { addTodo } = useContext(TodosContext);
  const [value, setValue] = useState("");
  const handleSubmit = (e) => {
    e.preventDefault();
    addTodo(value);
    setValue("");
  };
  return (
    <Form onSubmit={handleSubmit}>
      <InputGroup>
        <Input
          type="text"
          value={value}
          onChange={(e) => setValue(e.target.value)}
        />
        <InputGroupAddon addonType="append">
          <Button type="submit" color="primary">
            追加
          </Button>
        </InputGroupAddon>
      </InputGroup>
    </Form>
  );
}
export default TodoForm;

##参考サイト
[最短・最速で学ぶ Firebase Hosting + React Todoアプリ実装編 (React Hooks)]
(https://www.udemy.com/course/firebase-hosting-todo-reactreact-hooks/learn/lecture/18800736#overview)
[firebase-toolsをインストールするために新しくanyenvのパスを通した件]
(https://e-yota.com/webservice/firebase-tools_install_anyenv/)

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?