React Todo 应用程序通常是非常基础的——事实上,如果你是 React 初学者,并且想要提升你的技能,构建这类项目是一个很好的练习方式。
但是,你是否曾经构建过一个 Todo 应用程序,用户可以在其中使用语音命令添加待办事项。这使它变得更加复杂和令人兴奋。
这就是我们在本教程中要做的。为了构建这个基于语音的 Todo 应用程序,我们将使用三个主要工具:
- React - 用于用户界面
- Firebase – 用于数据库
- Alan AI – 用于执行语音命令
那么,让我们开始吧。
如何使用 React 创建 Todo 应用 UI
让我们首先创建一个 React 应用程序。只需输入以下命令:
npx create-react-app react-todo-alan-firebase
它将像这样初始化并创建我们的 React 应用程序。然后,我们将导航到该文件夹,并使用 npm start 启动应用程序。
现在让我们创建一个名为 components 的文件夹。它将包含我们的主要组件 Todo.js。 如下创建一个 Todo.js 文件。
为应用程序提供一个标题(或名称),例如基于语音的 Todo 应用程序或你选择的任何内容。
import React from 'react'
export default function Todo() {
return (
<div>
<h2>Voice-based Todo Application</h2>
</div>
)
}
然后,将此组件导入你的 App.js 文件。
让我们把标题居中。因此,在 Todo.js 组件中为 h2
指定一个 header 的 classname。
import React from 'react'
export default function Todo() {
return (
<div>
<h2 className="header">Voice-based Todo Application</h2>
</div>
)
}
然后我们将在 App.css 文件中添加一些样式,将标题居中。
.header{
text-align: center;
}
你将在屏幕上看到上面的输出,标题居中。
现在,让我们创建一张包含待办事项的卡片。
import React from 'react'
export default function Todo() {
return (
<div className="todo-main">
<h2 className="header">Voice-based Todo Application</h2>
<div className="todo-card">
</div>
</div>
)
}
创建一个 div 并使 className
为 todo-card
。你将看到最上级 div 的 className
为 todo-main
。这是因为我们需要一切内容都居中。
.todo-main {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.header {
text-align: center;
}
.todo-card {
border: 1px dashed #1f133d;
height: 50vh;
width: 50vh;
border-radius: 20px;
}
将上述样式添加到 App.css 中。现在看起来像这样:
现在让我们添加列表。
import React from 'react'
export default function Todo() {
return (
<div className="todo-main">
<h2 className="header">Voice-based Todo Application</h2>
<div className="todo-card">
<div className="todo-list">
<h3>
Wash the Clothes
</h3>
</div>
<div className="todo-list">
<h3>
Cook the Dinner
</h3>
</div>
<div className="todo-list">
<h3>
Code some software
</h3>
</div>
</div>
</div>
)
}
所以,我创建了一个 div,它包含 h3
标签中的项目。这些文本目前是静态的,但我们很快也会创建来自 Firebase 数据库的动态文本。
这是我们更新的样式:
.todo-main {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.header {
text-align: center;
}
.todo-card {
border: 1px dashed #1f133d;
height: 50vh;
width: 50vh;
border-radius: 20px;
}
.todo-list{
text-align: center;
}
这就是现在的输出,我们的列表中有三个项目。
现在,让我们添加一个关闭图标,它会在我们完成后删除每个项目。
为了添加图标,我们需要一个图标包。因此,让我们使用以下命令安装 React 图标:
npm install react-icons --save
安装后,从左侧边栏中选择任何图标包。
我正在使用 Feather Icons,所以我将导入它。
首先,让我们使用以下命令导入该包:
import { FiX } from "react-icons/fi";
然后,在 h3 标签后调用它。
import React from 'react'
import { FiX } from "react-icons/fi";
export default function Todo() {
return (
<div className="todo-main">
<h2 className="header">Voice-based Todo Application</h2>
<div className="todo-card">
<div className="todo-list">
<h3>
Wash the Clothes
</h3>
<FiX />
</div>
<div className="todo-list">
<h3>
Cook the Dinner
</h3>
<FiX />
</div>
<div className="todo-list">
<h3>
Code some software
</h3>
<FiX />
</div>
</div>
</div>
)
}
上面,这就是我们的应用程序现在的样子。但是我们希望关闭图标和待办事项在同一行。
为 Icon 指定一个 className 为 close-icon
。
<FiX className="close-icon" />
并在 App.css 中,添加以下样式:
.todo-list {
display: flex;
align-items: center;
justify-content: center;
}
.close-icon {
margin-left: 10px;
}
到目前为止,我们的 Todo.js 组件将具有以下最终代码:
import React from 'react'
import { FiX } from "react-icons/fi";
export default function Todo() {
return (
<div className="todo-main">
<h2 className="header">Voice-based Todo Application</h2>
<div className="todo-card">
<div className="todo-list">
<h3>
Wash the Clothes
</h3>
<FiX className="close-icon" />
</div>
<div className="todo-list">
<h3>
Cook the Dinner
</h3>
<FiX className="close-icon" />
</div>
<div className="todo-list">
<h3>
Code some software
</h3>
<FiX className="close-icon" />
</div>
</div>
</div>
)
}
App.css 是这样的:
.todo-main {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.header {
text-align: center;
}
.todo-card {
border: 1px dashed #1f133d;
height: 50vh;
width: 50vh;
border-radius: 20px;
}
.todo-list {
display: flex;
align-items: center;
justify-content: center;
}
.close-icon {
margin-left: 10px;
}
UI 看起来是这样:
如何把 Alan AI 添加到我们的 React 项目
访问 https://alan.app/,创建你的账号。
登录后,你可以创建一个项目。只需单击加号按钮。
但是在使用之前,我们需要先安装 Alan AI 包。因此,请访问 https://alan.app/docs/client-api/web/react,获取 React 文档。
使用以下命令安装 Alan Al:
npm install @alan-ai/alan-sdk-web --save
现在,让我们在主 App.js 文件中导入 Alan。
import alanBtn from "@alan-ai/alan-sdk-web";
然后,我们需要创建一个 useEffect Hook。每当我们的组件被挂载或加载时,它就会启动我们的 Alan 服务。
useEffect(() => {
alanBtn({
key: 'YOUR_KEY_FROM_ALAN_STUDIO_HERE',
onCommand: (commandData) => {
if (commandData.command === 'go:back') {
// Call the client code that will react to the received command
}
}
});
}, []);
这个 alanBtn
需要一个我们需要得到的密钥。因此,在你在 Alan 中创建的项目中,你应该会在顶部栏中看到一个 “Integrations” 按钮。 单击该按钮。
在那里你会得到你的密钥。
将该密钥粘贴到 React 应用程序中的 alanBtn
中,如下所示:
import './App.css';
import Todo from './components/Todo';
import alanBtn from "@alan-ai/alan-sdk-web";
import { useEffect } from 'react'
function App() {
useEffect(() => {
alanBtn({
key: '86e866fbe49666abd385ee5c9f9cbf5c2e956eca572e1d8b807a3e2338fdd0dc/stage',
onCommand: (commandData) => {
}
});
}, []);
return (
<div>
<Todo />
</div>
);
}
export default App;
现在,检查输出,你将看到一个麦克风按钮。
现在,我们需要在我们的 Alan 应用程序中创建一个 Intent。它将以添加命令开始,例如添加洗涤衣服、添加编写一些代码等。因此,让我们为此编写代码:
intent('Add $(item* (.*))', (p) => {
if(p.item.value){
p.play({ command: 'todoApp', data: p.item.value });
p.play(`${p.item.value} added`);
}
else{
p.play(`Cannot add Empty Item`);
}
})
它还会将项目返回给我们,我们可以在 React 应用程序中看到。在这里,我们还有一个阻止我们添加任何空项目的检查。如果我们尝试,它会回复“无法添加空项目”。
现在,我们希望将语音项接收回我们的 React 应用程序。
import './App.css';
import Todo from './components/Todo';
import alanBtn from "@alan-ai/alan-sdk-web";
import { useEffect } from 'react'
function App() {
useEffect(() => {
alanBtn({
key: '86e866fbe49666abd385ee5c9f9cbf5c2e956eca572e1d8b807a3e2338fdd0dc/stage',
onCommand: (commandData) => {
console.log(commandData)
}
});
}, []);
return (
<div>
<Todo />
</div>
);
}
export default App;
只需 console.log commandData,你将得到以下结果。不要忘记单击麦克风按钮并说点什么。你将在控制台中看到你所说的内容。
好了,我们的 Alan AI 现已准备就绪。
如何使用 Firebase 将数据发送到 Firestore 数据库
我们现在将这些数据发送到 Firebase。
但首先,我们需要安装它。访问 https://firebase.google.com/,并在那里创建一个项目。
要安装 Firebase,只需键入 npm install firebase
。
然后,在项目中创建一个应用程序,如下所示:
它会给我们一些配置数据。只需在 src 文件夹中创建一个文件,将其命名为 firebase-config.js
,然后添加这些配置数据。
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyCP8qL8z9BorGF3NZJsGb4vSaWHYyCVfc8",
authDomain: "todo-firebase-alan.firebaseapp.com",
projectId: "todo-firebase-alan",
storageBucket: "todo-firebase-alan.appspot.com",
messagingSenderId: "892581913000",
appId: "1:892581913000:web:dbe08ac753c3adaab87d9d"
};
// Initialize Firebase
export const app = initializeApp(firebaseConfig);
不要忘记导出 const 应用程序。
接下来,我们还需要访问 Firestore。所以,让我们将它导入到我们的 firebase-config.js 文件中。
import { getFirestore } from 'firebase/firestore'
export const database = getFirestore(app);
然后,在底部导出它。
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getFirestore } from 'firebase/firestore'
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyCP8qL8z9BorGF3NZJsGb4vSaWHYyCVfc8",
authDomain: "todo-firebase-alan.firebaseapp.com",
projectId: "todo-firebase-alan",
storageBucket: "todo-firebase-alan.appspot.com",
messagingSenderId: "892581913000",
appId: "1:892581913000:web:dbe08ac753c3adaab87d9d"
};
// Initialize Firebase
export const app = initializeApp(firebaseConfig);
export const database = getFirestore(app);
这是整个 firebase-config 代码。
现在,在 App.js 中,我们需要导入这个应用程序和数据库。
import { app, database } from './firebase-config';
接下来,我们需要创建与 Firebase Firestore 的连接。为此,我们需要 Firebase Firestore 的集合属性。此外,我们将导入 addDoc,我们将使用它向 Firestore 添加数据。
import { collection, addDoc } from 'firebase/firestore';
现在,让我们创建与数据库的连接。
创建一个变量,并将我们从 firebase-config 文件导入的数据库以及我们想要给集合的名称放入数据库中。由于我们希望集合是待办事项列表,所以可以添加以下内容:
const databaseRef = collection(database, 'todo-list');
要添加数据,我们需要 addDoc 属性。
addDoc 属性将采用两个参数。第一个是我们创建的连接,databaseRef。第二个是我们要添加的数据,作为一个对象。
将 addDoc 放入 useEffect Hook 中,如下所示:
useEffect(() => {
alanBtn({
key: '86e866fbe49666abd385ee5c9f9cbf5c2e956eca572e1d8b807a3e2338fdd0dc/stage',
onCommand: (commandData) => {
addDoc(databaseRef, { item: commandData.data })
}
});
}, []);
目前,我们的 Firestore 是空的。
现在,让我们试试这个。从 add 命令开始对着麦克风说话,它会在 Firebase Firestore 中可见。
可以看到,我们所说的现在已添加到 Firebase 中。
现在,让我们尝试读取并显示这些数据。
将 databaseRef
作为 prop 传递给 Todo 组件。
<Todo databaseRef={databaseRef}/>
然后在 Todo 组件中接收它。
export default function Todo({databaseRef})
在 Todo.js 组件中创建一个 useEffect 钩子,并在 useEffect 钩子中创建函数 getData
。
useEffect(() => {
const getData = async () => {
}
getData()
}, [])
我们将使用 getDocs 属性从 Firebase Firestore 读取数据。我们还需要连接 databaseRef,之前将它作为 prop 传递。
let data = await getDocs(databaseRef);
const getData = async () => {
let data = await getDocs(databaseRef);
console.log(data.docs.map((item) => ({ ...item.data(), id: item.id })));
}
我们映射传入的数据以使其更具可读性,还添加了项目的唯一 ID,应用程序稍后将用它删除该项目。
现在让我们检查控制台:
我们就要完成项目啦。
现在,让我们将这些数据存储在 state 中,在 React 应用程序中显示。
导入 useState Hook,然后像这样创建一个数组状态:
const [todoList, setTodoList] = useState([]);
并使用 setTodoList
函数设置数据:
setTodoList(data.docs.map((item) => ({ ...item.data(), id: item.id })));
现在,让我们映射 todoList 数组。
<div className="todo-main">
<h2 className="header">Voice-based Todo Application</h2>
<div className="todo-card">
{todoList.map((todo) => {
return (
<div className="todo-list">
<h3>
{todo.item}
</h3>
<FiX className="close-icon" />
</div>
)
})}
</div>
</div>
我们将在 React UI 中看到我们的数据,它看起来像这样:
现在,让我们更新它,使每个 Todo 都以大写字母开头。
设置 h3 标签的 className
为 todo-items
。
<h3 className="todo-item">
{todo.item}
</h3>
在 App.css 中,添加以下样式:
.todo-item{
text-transform: capitalize;
}
你会看到每个 Todo 现在都大写了。
现在,如果我们通过语音命令添加任何内容,在 React 中的列表应该更新。因此,让我们将 useEffect 配置为每次我们对应用程序说话时运行。
在 App.js 文件中,创建一个状态。它将是一个布尔值,初始状态为 false。
const [update, setUpdate] = useState(false)
当我们说些什么,或者 App.js 中的 useEffect 运行时,这个状态会变成 true。
useEffect(() => {
alanBtn({
key: '86e866fbe49666abd385ee5c9f9cbf5c2e956eca572e1d8b807a3e2338fdd0dc/stage',
onCommand: (commandData) => {
addDoc(databaseRef, { item: commandData.data })
.then(() => {
setUpdate(true);
})
}
});
}, []);
然后,我们将在 Todo.js 中传递更新状态和更新状态的函数。
<Todo databaseRef={databaseRef} update={update} setUpdate={setUpdate}/>
并在 Todo 组件中接收这两个。
export default function Todo({ databaseRef, update, setUpdate })
然后,在 Todo.js 的 useEffect 中,一旦它从 Firebase Firestore 获取我们的数据,就使用 setUpdate 函数将更新设置为 false,然后将更新状态放入依赖数组中。
useEffect(() => {
const getData = async () => {
let data = await getDocs(databaseRef);
setTodoList(data.docs.map((item) => ({ ...item.data(), id: item.id })));
}
getData()
setUpdate(false)
}, [update])
这可能有点令人困惑,让我来解释一下。
当我们说话时,更新状态从 false 变为 true。然后,当完成从 Firestore 获取数据时,它会从 true 更改为 false。这样,状态就会不断变化。 所以每次更新状态改变时 useEffect 都会更新。
让我们现在试试看。说点什么,列表会动态更新。
现在,让我们添加删除函数以从 Firebase Firestore 和我们的 React 应用程序中删除项目。
创建一个名为 deleteItems
的函数。
const deleteItems = () => {
}
并将此函数绑定到 close
图标,如下所示:
<FiX className="close-icon" onClick={() => deleteItems()}/>
当我们点击一个特定的关闭图标时,我们需要将该项目的 id 传递给函数,以删除该项目。
<FiX className="close-icon" onClick={() => deleteItems(todo.id)}/>
并在函数中接收它。
让我们尝试 console.log 我们的 id:
const deleteItems = (id) => {
console.log(id)
}
我们将在控制台中获得该特定 id。
现在,在删除任何待办事项之前,我们需要指定要删除的待办事项。因此,我们将使用此 id 创建一个引用。我们将使用 Firestore 的 doc 属性。
因此,首先使用以下命令从 Firestore 导入文档:
import { getDocs, doc } from 'firebase/firestore';
然后,在函数 1deleteItems1 中,添加以下代码:
const data = doc(database, 'todo-list', id)
该文档具有三个参数——数据库、集合名称和 id。我们有这三样东西。
数据库已从 firebase-config 导入。待办事项列表是 Firestore 集合的名称,以及我们从关闭按钮点击中获得的 id。
要删除一个项目,我们需要来自 Firestore 的另一个名为 deleteDoc 的属性。
import { getDocs, doc, deleteDoc } from 'firebase/firestore';
然后,只需添加以下内容:
const deleteItems = (id) => {
const data = doc(database, 'todo-list', id);
deleteDoc(data)
}
立即尝试——单击关闭图标,然后检查 Firestore。该项目将被删除。
但是我们在添加和读取操作期间遇到了同样的问题:在我们删除一个项目后,React 应用程序没有得到更新。
因此,首先要做的是将 getData 函数移到 useEffect Hook 之外。别担心,它仍然有效。
const getData = async () => {
let data = await getDocs(databaseRef);
setTodoList(data.docs.map((item) => ({ ...item.data(), id: item.id })));
}
useEffect(() => {
getData()
setUpdate(false)
}, [update])
而在 deleteDoc 函数中,我们需要再次调用 getData 函数,在用户删除一个 item 后获取更新的数据。
const deleteItems = (id) => {
const data = doc(database, 'todo-list', id);
deleteDoc(data)
.then(() => {
getData()
})
}
这是 Todo.js 的全部代码:
import React, { useEffect, useState } from 'react'
import { FiX } from "react-icons/fi";
import { database } from '../firebase-config';
import { getDocs, doc, deleteDoc } from 'firebase/firestore';
export default function Todo({ databaseRef, update, setUpdate }) {
const [todoList, setTodoList] = useState([]);
const getData = async () => {
let data = await getDocs(databaseRef);
setTodoList(data.docs.map((item) => ({ ...item.data(), id: item.id })));
}
useEffect(() => {
getData()
setUpdate(false)
}, [update])
const deleteItems = (id) => {
const data = doc(database, 'todo-list', id);
deleteDoc(data)
.then(() => {
getData()
})
}
return (
<div className="todo-main">
<h2 className="header">Voice-based Todo Application</h2>
<div className="todo-card">
{todoList.map((todo) => {
return (
<div className="todo-list">
<h3 className="todo-item">
{todo.item}
</h3>
<FiX className="close-icon" onClick={() => deleteItems(todo.id)} />
</div>
)
})}
</div>
</div>
)
}
这是 App.js 的代码:
import './App.css';
import Todo from './components/Todo';
import alanBtn from "@alan-ai/alan-sdk-web";
import { useEffect, useState } from 'react';
import { app, database } from './firebase-config';
import { addDoc, collection } from '@firebase/firestore';
function App() {
const databaseRef = collection(database, 'todo-list');
const [update, setUpdate] = useState(false)
useEffect(() => {
alanBtn({
key: '86e866fbe49666abd385ee5c9f9cbf5c2e956eca572e1d8b807a3e2338fdd0dc/stage',
onCommand: (commandData) => {
addDoc(databaseRef, { item: commandData.data })
.then(() => {
setUpdate(true);
})
}
});
}, []);
return (
<div>
<Todo databaseRef={databaseRef} update={update} setUpdate={setUpdate} />
</div>
);
}
export default App;
现在,我们可以使用语音命令添加项目,并将其存储在我们的 Firebase 数据库中。它也会出现在我们的 React 应用程序中。
还有最后一件事要做。在我们的 App.css 文件中,我们需要将卡片的高度设置为 auto,以防止文本溢出。
.todo-card {
border: 1px dashed #1f133d;
height: auto;
width: 50vh;
border-radius: 20px;
}
这只是一个简单的 UI 设计。如果需要,你可以设计自己喜欢的。来吧,构建这个很棒的应用程序。
你可以在我的 YouTube 频道查看视频 Let's build a Voice-Based Todo Application using React, Firebase, and Alan AI。
谢谢阅读,祝你学习愉快!
原文:How to Build a Voice-Based Todo App using React, Firebase, and Alan AI,作者:Nishant Kumar