透過開發2023年度的【React x Firebase】Todo應用程式,來學習
首先
这次作为学习React的第一步,我尝试创建了一个”Todo应用”。我使用了两年前在YouTube上的视频中无法使用的代码等。
我們的目標是實現「登入/登出功能」以及相關的「用戶狀態管理」。通過使用Firebase Firestore,我們可以實現保存「用戶信息」和「待辦事項信息」的功能。
由于尚未编写Firestore规则,此网络应用程序尚不完善,但最终产品如下所示。
这次的成品
https://react-todolist-a2dda.web.app/

实际操作指南的资料
我相信总会有人想要学习,所以我将实践操作的步骤按照每个阶段进行了说明。
如果只是制作的话,可以通过复制粘贴并修改数值来完成,但我个人认为了解制作过程在编程学习中也是重要的,所以我会把它写上。
由於這篇文章的解說不足,我會補充一些內容。
↑↑↑我将解释CodeLab形式的解释方法。
React的参考资料
官方网站
在学习React的过程中,我也经常访问React官方网站。我认为,基本上在这里查找所需的信息是很好的。
个人观点是,值得注意的网站
当你想直观地理解React是什么时,以下资料是很好的参考。
在开始学习之前,我建议先轻松地浏览一下”#01″和”#02″,或者尝试一些实践。
必须的控制台操作和代码完成品
需要作为前提条件的东西
查看节点版本
node -v
我认为在VScode的cmd内执行以下命令是更好的选项。
npm create-react-app . = 使用npm创建一个React应用程序。
npm create-react-app ファイル名 = 使用npm创建一个名为ファイル名的React应用程序。
学习React时,请先执行这个命令。
在当前文件夹下创建↓↓↓
npm creat-react-app .
如果你在使用cd命令等移动到一个文件夹内部,想要新建一个文件夹的话↓↓↓
npm creat-react-app <ファイル名>
使用npm安装react-router-dom
这是网站内页面跳转所必需的东西。
安装bootstrap的npm包
这是网站装饰所需的物品。
数据库设计

对于用户1来说,他有多个待办事项的信息。
文件结构
在这里这大部分需要创建的文件都有了,实际上使用create-react-app命令会生成一些文件和文件夹,但不需要的可以删除。
C:react-todolist/src
│ App.css
│ App.js
│ index.css
│ index.js
│ reportWebVitals.js
│ setupTests.js
│
├─Components
│ Header.js
│ Home.js
│ IndividualTodo.js
│ Login.js
│ Modal.js
│ NotFound.js
│ Signup.js
│ Todos.js
│
├─images
│ TodoAppicon.png
│
└─services
firebase.config.js
代码的完整产品 (Code’s finished product)
如果你按照实际操作指南的资料进行操作,基本上应该不会有问题。
只需进行必要的安装,创建文件夹和文件,然后复制粘贴,就可以完成了。
源代码 / App.js
从’react’中导入React和Component
从’./index.css’中导入’./Components/Home’和’./Components/Login’和’./Components/Signup’和’./Components/NotFound’并命名为Home和Login和Signup和NotFound
从’./services/firebase.config’中导入auth和db
从’firebase/firestore’中导入doc和getDoc和deleteDoc和query和where和collection和onSnapshot
创建一个名为App的类,继承Component类
状态 = {
currentUser: null,
todos: [],
editTodoValue: null
}
componentDidMount() {
auth.onAuthStateChanged(user => {
if (user) {
getDoc(doc(db, ‘users’, user.uid)).then(snapshot => {
this.setState({
currentUser: snapshot.data().userName
})
})
}
else {
console.log(“未登录用户”)
}
})
auth.onAuthStateChanged(user => {
if (user) {
const todoList = [];
const q = query(
collection(db, ‘todos of’ + user.uid),
where(‘userId’, ‘==’, user.uid)
);
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach((change) => {
if (change.type === ‘added’) {
todoList.push({
id: change.doc.id,
Todo: change.doc.data().Todo,
});
}
if (change.type === ‘removed’) {
//console.log(change.type);
for (let i = 0; i < todoList.length; i++) { if (todoList[i].id === change.doc.id) { todoList.splice(i, 1); } } } }); // console.log(‘待办事项列表:’, todoList); this.setState({ todos: todoList }); }); return unsubscribe; } else { console.log(‘未登录用户’); } }); } deleteTodo = (id) => {
// console.log(id);
auth.onAuthStateChanged((user) => {
if (user) {
const docRef = doc(db, ‘todos of’ + user.uid, id);
deleteDoc(docRef)
.then(() => {
console.log(‘文档删除成功!’);
})
.catch((error) => {
console.error(‘删除文档时出错: ‘, error);
});
} else {
console.log(‘未登录用户’);
}
});
};
editModal = (obj) => {
this.setState({
editTodoValue: obj
})
}
updateTodoHandler = (editTodo, id) => {
// console.log(editTodo, id);
const todoList = this.state.todos;
for(let i=0;i<todoList.length;i++){
if(todoList[i].id===id){
todoList.splice(i,1,{id,Todo: editTodo});
}
this.setState({
todos:todoList
})
}
}
render() {
返回 (
} />
} />
} />
} />
)
}
}
导出默认的App
源码/索引.css
* {
边距: 0;
填充: 0;
}div.包装器 {
横向溢出: 隐藏;
纵向溢出: 自动;
}
/* 头部 */
.头部-盒子 {
宽度: 100%;
高度: 自动;
填充: 50px;
显示: 弹性;
对齐内容: 空隙分布;
对齐项目: 居中;
背景颜色: #0170ad;
}
@media(max-width:768px){
.头部-盒子{
弹性方向: 垂直;
对齐内容: 居中;
}
}
.头部-盒子 .左侧 {
弹性: 1;
显示: 弹性;
对齐内容: 弹性开始;
对齐项目: 居中;
}
@media(max-width: 768px){
.头部-盒子 .左侧{
宽度: 100%;
弹性方向: 垂直-反向;
对齐内容: 居中;
文本对齐: 中心;
下边距: 20px;
}
}
.头部-盒子 .左侧 .图像 {
宽度: 170px;
高度: 170px;
}
.头部-盒子 .左侧 .图像 图片 {
宽度: 100%;
高度: 100%;
}
.头部-盒子 .左侧 .内容 {
颜色: #fff;
}
.头部-盒子 .左侧 .内容 .大标题 {
字体大小: 42px;
}
.头部-盒子 .左侧 .内容 .小标题 {
字体大小: 24px;
}
.头部-盒子 .右侧 {
弹性: 1;
显示: 弹性;
弹性方向: 垂直;
对齐内容: 居中;
对齐项目: 居中;
}
@media(max-width:768px){
.头部-盒子 .右侧{
宽度: 100%;
文本对齐: 中心;
}
}
.头部-盒子 .右侧 .按钮, .头部-盒子 .右侧 .按钮:hover{
宽度: 100px;
下边距: 5px;
颜色: #fff;
文本装饰: 无;
}
.日期-区块{
颜色: #fff;
}
.日期-区块 span{
左边距: 4px;
}
/* 注册 */
@media(max-width:539px){
.注册{
宽度:100%
}
}
.错误-消息{
颜色: 红色;
宽度:100%;
字体大小:14px;
字体粗细: 600;
}
.欢迎-区块{
颜色: #fff;
字母间距: 0.09em;
}
/* 待办事项 */
.待办事项{
背景颜色: #e4e4e4;
字体粗细: 600;
字体大小: 16px;
边距: 10px 0px;
填充: 10px;
显示: 弹性;
对齐内容: 空隙分布;
对齐项目: 居中;
}
.待办事项 .操作-区块{
显示: 弹性;
对齐内容: 弹性开始;
对齐项目: 居中;
}
.待办事项 .操作-区块 div{
边距: 0px 10px;
光标: 指针;
}
.删除-按钮{
颜色: rgb(165, 2, 2);
光标: 指针;
}
.模态-容器{
宽度: 100%;
高度: 100vh;
位置: 固定;
顶部: 0;
左侧: 0;
背景颜色: rgba(0,0,0,0.7);
显示: 弹性;
对齐内容: 居中;
对齐项目: 居中;
}
.模态-容器 .模态{
显示: 块;
宽度: 70%;
高度: 70vh;
背景颜色: #fff;
边框半径: 20px;
边距: 60px 15%;
}
@media(max-width: 768px){
.模态-容器 .模态{
宽度: 100%;
高度: 100vh;
边距: 0;
边框半径: 0px;
}
}
.模态-容器 .模态 .头部{
宽度: 100%;
高度: 自动;
填充: 20px;
显示: 弹性;
对齐内容: 空隙分布;
对齐项目: 居中;
}
.模态-容器 .模态 .头部 .更新-文本{
字体大小: 24px;
字体粗细: 600;
宽度: 100%;
}
.模态-容器 .模态 .头部 .关闭-按钮{
颜色: rgb(165, 2, 2);
光标: 指针;
}
组件/ Header.js
import React, { useEffect, useState } from ‘react’
import { Link } from ‘react-router-dom’
import todoIcon from ‘../images/TodoAppicon.png’
import ‘bootstrap/dist/css/bootstrap.css’
import ‘../index.css’
import {auth} from ‘../services/firebase.config’
export const Header = ({ currentUser }) => {
const [year, setYear] = useState(null);
const [date, setDate] = useState(null);
const [month, setMonth] = useState(null);
const [day, setDay] = useState(null);
useEffect(() => {
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
const currentDateOfMonth = currentDate.getDate();
const currentMonth = currentDate.toLocaleString(‘ja-JP’, { month: ‘long’ });
const currentDay = currentDate.toLocaleDateString(‘js-JP’, { weekday: ‘long’ });
setYear(currentYear);
setDate(currentDateOfMonth);
setMonth(currentMonth);
setDay(currentDay);
}, [])
const handleLogout = () => {
auth.signOut().then(() => {
window.location.reload();
});
}
return (
{!currentUser && <>
注册
登录
{month}
{date}日
{day}
</>}
{currentUser &&
欢迎
{currentUser}
{month}
{date}日
{day}
}
)
}
组件 / Home.js
import React, { useState } from ‘react’
import ‘../index.css’
import { Header } from ‘./Header’
import { Todos } from ‘./Todos’
import { Modal } from ‘./Modal’
import { auth, db } from ‘../services/firebase.config’
import { collection, addDoc } from ‘firebase/firestore’
export const Home = ({ currentUser, todos,deleteTodo,editTodoValue,editModal,updateTodoHandler }) => {
const [todo, setTodo] = useState(”);
const [todoError, setTodoError] = useState(”);
const handleTodoSubmit = async (e) => {
e.preventDefault();
await auth.onAuthStateChanged(user => {
if (user) {
addDoc(collection(db, ‘用户’ + user.uid + ‘的待办事项’), {
待办事项: todo,
用户ID: user.uid
}).then(setTodo(”)).catch(err => setTodoError(err.message))
}
else {
console.log(“用户未登录”);
}
})
}
return (
{todoError &&
}
{editTodoValue && }
)
}
组件 / IndividualTodo.js
import React from ‘react’
import {FiEdit} from ‘react-icons/fi’
import {FaTrashAlt} from ‘react-icons/fa’
export const IndividualTodo = ({ individualTodo,deleteTodo,editModal }) => {
const handleDelete=()=>{
deleteTodo(individualTodo.id);
}
const handleEditModal=()=>{
editModal(individualTodo);
}
return (
)
}
组件 / 登录.js
import React, { useState } from ‘react’
import { Link, useNavigate } from ‘react-router-dom’
import { signInWithEmailAndPassword } from ‘firebase/auth’;
import { auth } from ‘../services/firebase.config’
export const Login = () => {
const [email, setEmail] = useState(”);
const [password, setPassword] = useState(”);
const [loginError, setLoginError] = useState(”);
const navigate = useNavigate();
const handleLogin = async (e) => {
e.preventDefault();
try {
await signInWithEmailAndPassword(auth, email, password);
setEmail(”);
setPassword(”);
setLoginError(”);
navigate(‘/’);
} catch (error) {
setLoginError(error.message);
}
};
return (
在这里登录
{loginError &&
}
还没有账户? 创建一个
这里
)
}
组件 / Modal.js
import React, { useState } from ‘react’
import { FiXCircle } from ‘react-icons/fi’
import { doc, updateDoc } from “firebase/firestore”;
import { db, auth } from ‘../services/firebase.config’
export const Modal = ({ editTodoValue, editModal,updateTodoHandler }) => {
const [editTodo, setEditTodo] = useState(editTodoValue.Todo);
const handleClose = () => {
editModal(null)
}
const handleEditTodoSubmit = async (e) => {
e.preventDefault();
handleClose();
updateTodoHandler(editTodo, editTodoValue.id);
await auth.onAuthStateChanged(user => {
if (user) {
const todoRef = doc(db, ‘todos of’ + user.uid,editTodoValue.id);
updateDoc(todoRef, {
Todo: editTodo
})
}
else {
console.log(“user is not signed”);
}
})
}
return (
)
}
组件 / NotFound.js
import React from ‘react’
export const NotFound = () => {
return (
)
}
组件 / 注册.js
import React, { useState } from ‘react’;
import { Link } from ‘react-router-dom’;
import { useNavigate } from ‘react-router-dom’;
import { createUserWithEmailAndPassword } from ‘firebase/auth’;
import { doc, setDoc } from ‘firebase/firestore’;
import { auth, db } from ‘../services/firebase.config’;
export const Signup = () => {
const [userName, setUserName] = useState(”);
const [email, setEmail] = useState(”);
const [password, setPassword] = useState(”);
const [registerError, setRegisterError] = useState(”);
const navigate = useNavigate();
const handleRegister = async (e) => {
e.preventDefault();
try {
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
const { uid } = userCredential.user;
console.log(userName, email, password, uid);
await setDoc(doc(db, ‘users’, uid), {
userName: userName,
Email: email,
Password: password,
});
setUserName(”);
setEmail(”);
setPassword(”);
setRegisterError(”);
navigate(‘/login’);
} catch (error) {
setRegisterError(error.message);
}
};
return (
现在注册!
{registerError && (
)}
已经拥有账户? 在这里登录
);
};
组件/ Todos.js
import React from ‘react’
import { IndividualTodo } from ‘./IndividualTodo’
export const Todos = ({todos,deleteTodo,editModal}) => {
return todos.map((individualTodo)=>{
return (
)
})
}
服务 / firebase.config.js
import { initializeApp } from ‘firebase/app’
import { getAuth } from ‘firebase/auth’
import { getFirestore } from ‘firebase/firestore’
const firebaseConfig = {
apiKey: “你的配置代码”,
authDomain: “你的配置代码”,
projectId: “你的配置代码”,
storageBucket: “你的配置代码”,
messagingSenderId: “你的配置代码”,
appId: “你的配置代码”,
measurementId: “你的配置代码”
};
const app = initializeApp(firebaseConfig)
const auth = getAuth(app)
const db = getFirestore(app)
export { auth, db }
结束
通过制作Todo应用并添加各种功能,我能够理解React和Firestore数据库操作的各种创建方法。
想要制作想象中的事物,需要相应的先决知识,所以学习是不可或缺的。
我之前制作了一个学食售罄信息确认应用程序,名为“随时检查午餐”,但在学习React的过程中,我意识到与仅使用JavaScript相比,使用React可以更容易地按功能进行创建并进行管理。因此,我希望今后能使用React进行制作。
各位,在现代(2023/05/22)这个时代,有许多信息可以供您参考,请您尝试自行查询或咨询。