##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
import 'bootstrap/dist/css/bootstrap.min.css';
##Formの作成 / 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;
##リスト表示 + 削除ボタンの追加/完了・未完了変更処理の追加
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
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;
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;
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を使ったグローバルステート管理
#####contexパターンの実装方法
#####contexts ディレクトリを src ディレクトリ配下に配置する
#####TodosContextsを定義し、ファイルを用意する
$ mkdir src/contexts
$ touch src/contexts/TodosContext.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;
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;
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;
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/)