使用 React 和 Firebase 在 5 分钟内构建实时聊天应用程序
目录
我们正在建造什么?
创建 React 应用
创建 Firebase 项目
定义 UI
Google 身份验证
从 Firestore 实时读取数据
将数据添加到 Firestore
奖金
下一步是什么?
结论
成为一名 React 开发者
在此视频/文章中,我将向您展示如何使用 React 和 Firebase 在 5 分钟内构建像 Discord 或 Slack 这样的强大的实时聊天应用程序。
您将学习如何使用 Google 验证用户身份,以及如何使用 Cloud Firestore 处理实时数据。此外,课程最后还有福利😉
出发啦!🚀🔥
在 Youtube 上观看视频或继续阅读。
目录
- 我们正在建造什么?
- 创建 React 应用
- 创建 Firebase 项目
- 定义 UI
- Google 身份验证
- 从 Firestore 实时读取数据
- 将数据添加到 Firestore
- 奖金
- 下一步是什么?
- 结论
- 成为一名 React 开发者
我们正在建造什么?
在我们开始之前,让我们快速浏览一下我们今天要构建的内容。
这是一款简单但功能强大的聊天应用,每个人都可以使用短信进行交流。它就像一个 Discord 服务器或一个带有单一频道的 Slack 聊天室。
要进入聊天并与他人互动,用户必须使用他的 Google 帐户登录。
登录后,用户可以阅读聊天中的先前消息并通过发送消息开始聊天。
所有消息都存储在 Firestore 数据库中,并与我们的 React 应用程序实时同步。
那么,开始吧!我会设置一个计时器,只需 5 分钟,你就能拥有一个可以运行的实时聊天应用程序。
记住,视频最后会演示如何通过三个简单的步骤免费部署你的应用!所以,坚持到最后,你就能获得一个上线的应用。
创建 React 应用
首先,我们需要创建一个新的 React 应用。为此,我们将使用create-react-app工具,仅用一行命令行即可设置我们的应用。
因此,让我们继续使用我们的应用程序名称React FireChat运行该命令:
npx create-react-app react-firechat
完成后,我们可以安装依赖项以与 Firebase 配合使用,即 Firebase JS SDK。
npm install --save firebase
现在,让我们继续打开App.js
文件并删除所有已经存在的样板代码。
function App() {
return <div></div>;
}
export default App;
然后,将 Firebase SDK 与 Firestore 一起导入作为我们的数据库,并将 firebase auth 导入用于用户身份验证。
// Firebase deps
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
最后,我们需要通过传入 Firebase 项目配置来在应用中初始化 Firebase。要获取此配置,我们需要一个 Firebase 项目。所以,让我们创建一个。
firebase.initializeApp({ ... });
创建 Firebase 项目
要创建新的 Firebase 项目,请转到 Firebase 控制台,单击“添加项目”,然后按照步骤操作。
然后,为了允许用户使用他们的 Google 帐户登录,我们需要从项目的身份验证页面启用 Google 登录方法。
要使用 Firestore 作为存储消息的数据库,我们需要从 Cloud Firestore 页面启用它。
最后,我们需要做的最后一件事是向我们的 Firebase 项目添加一个新的 Web 应用。完成后,您将获得一个 JavaScript 对象,该对象代表您刚刚创建的 Firebase 项目应用的配置。
因此继续复制此配置对象并将其粘贴到initializeApp
源代码中。
好了!现在我们应该已经准备好构建聊天应用了。我们创建了 React 应用和 Firebase 项目,并将两者关联起来。
因此让我们构建我们的应用程序的视图。
定义 UI
但首先,让我们看看我们的应用程序是如何构建的,并将 UI 分解为组件层次结构。
在这个应用中,如果用户已登录,我们会显示聊天记录和消息列表。否则,我们会显示一个按钮,让用户使用 Google 账户登录。
除了主App
组件之外,我们还需要创建另外 3 个 React 组件。一个用于登录和退出按钮,一个用于包含消息列表的频道,以及一个用于消息本身。
Google 身份验证
登录按钮
那么让我们从登录按钮开始。
这个组件非常简单。它是一个接受 onClick 属性的简单按钮。
const Button = ({ onClick = null, children = null }) => (
<button onClick={onClick}>{children}</button>
);
export default Button;
从App
组件中,我们现在可以渲染它并实现onClick
事件处理程序。
function App() {
const signInWithGoogle = async () => {
// Retrieve Google provider object
const provider = new firebase.auth.GoogleAuthProvider();
// Set language to the default browser preference
firebase.auth().useDeviceLanguage();
// Start sign in process
try {
await firebase.auth().signInWithPopup(provider);
} catch (error) {
console.log(error.message);
}
};
return (
<div>
<Button onClick={signInWithGoogle}>Sign in with Google</Button>;
</div>
);
}
在此事件处理程序中,我们使用来自 Firebase 的 Google Auth 提供程序启动登录过程,然后将语言设置为用户的首选语言,并调用该signInWithPopup
方法提示用户通过弹出窗口登录。
好的,现在我们需要一种方法来检查用户是否已通过身份验证。在这种情况下,我们不显示登录按钮,而是让他直接进入聊天。
我们可以通过该属性从 Firebase 快速访问经过auth.currentUser
身份验证的用户。
const [user, setUser] = useState(() => auth.currentUser);
但是与许多应用程序一样,您也需要知道用户当前是否已登录或退出您的应用程序。Firebase 提供了一种名为 的方法onAuthStateChanged
,允许您订阅用户当前的身份验证状态,并在该状态发生变化时接收事件。
设置这样的事件监听器是我们应该在useEffect
React hooks 内部做的事情,因为它是一个副作用。
因此让我们继续进行设置。
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState(() => auth.currentUser);
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
if (initializing) {
setInitializing(false);
}
});
// Cleanup subscription
return unsubscribe;
}, [initializing]);
此方法返回一个函数,我们可以使用它在组件卸载时清理订阅。
还有一件事,onAuthStateChanged
监听器是异步的,一旦与 Firebase 建立连接就会触发初始状态。
因此,设置“初始化”状态以在建立连接时阻止我们的主要应用程序进行渲染至关重要。
用户首次登录后,系统会创建一个新的用户帐号,并将其存储为您的 Firebase 项目的一部分。无论用户如何登录,该帐号都可用于在项目中的每个应用中识别用户。
退出按钮
太棒了!现在让我们添加一个退出按钮,让用户可以退出我们的应用。
要注销用户,我们只需signOut
从 Firebase SDK 调用该方法即可。由于我们已经设置了一个监听器来监听任何身份验证更改,因此用户状态应该会自动更新并设置为null
,从而触发应用的重新渲染,并再次显示登录按钮。
function App() {
const signInWithGoogle = async () => { ... };
const signOut = async () => {
try {
await firebase.auth().signOut();
} catch (error) {
console.log(error.message);
}
};
return (
<div>
{
user ? (
<>
<Button onClick={signOut}>Sign out</Button>
<p>Welcome to the chat!</p>
</>
) : <Button onClick={signInWithGoogle}>Sign in with Google</Button>
}
</div>
);
}
从 Firestore 实时读取数据
现在,让我们开始创建Channel
组件。
该组件负责显示聊天消息和输入字段,以允许用户向聊天发送新消息。
但首先,我们需要从 Firebase 控制台设置我们的 Firestore 数据库。
Cloud Firestore 将数据存储在“文档”中,而“文档”又包含在“集合”中。在我们的例子中,我们将用户的聊天消息存储在“消息”集合中。
让我们继续并在该集合中创建我们的第一个文档。
目前,我们只需向此新文档添加两个字段:消息本身的文本以及表示消息创建日期和时间的时间戳。
太棒了!这就是我们所需要的。让我们回到我们的应用程序。
在我们的Channel
组件中,我们需要查询 Firestore 来检索消息,并像往常一样使用 JSX 将它们渲染到屏幕上。因此,我们首先创建一个状态变量来存储消息。
const Channel = ({ user = null }) => {
const [messages, setMessages] = useState([]);
return <ul></ul>;
};
export default Channel;
查询数据是副作用。因此,我们将从useEffect
钩子内部获取消息。
Cloud Firestore 提供了读取集合或文档值的功能。这可以一次性完成,也可以在查询中的数据发生变化时通过监听实时更新来完成。
在我们的例子中,我们只对实时更新感兴趣。我们使用 Firestore 来实现这一点,即设置一个主动监听器,使用onSnapshot
带有事件处理程序回调的方法,来响应我们想要执行的查询的任何更改。
为了观察“消息”集合中的任何变化,我们创建以下查询。
const db = firebase.firestore();
const query = db.collection('messages').orderBy('createdAt').limit(100);
我们获取对集合的引用,按createdAt
属性对其进行排序,最后,我们限制从查询返回的消息数量。
这个由你决定。为了简单起见,我们限制消息数量为 100 条。你也可以使用查询游标分批读取消息。
现在我们有了查询,我们可以用该onSnapshot
方法设置事件监听器了。它还返回一个函数,允许我们取消订阅事件,以便在组件卸载时清除副作用。
useEffect(() => {
// Subscribe to query with onSnapshot
const unsubscribe = query.onSnapshot(querySnapshot => {
...
});
// Detach listener
return unsubscribe;
}, []);
一旦查询返回结果,Firestore 就会返回一个QuerySnapshot
。这些快照使我们能够实际获取通过查询请求的数据。
要访问查询返回的所有文档,我们可以从 docs 属性中获取它,QuerySnapshot
并循环获取每个文档的数据以及文档 ID。
useEffect(() => {
// Subscribe to query with onSnapshot
const unsubscribe = query.onSnapshot(querySnapshot => {
// Get all documents from collection - with IDs
const data = querySnapshot.docs.map(doc => ({
...doc.data(),
id: doc.id,
}));
});
// Detach listener
return unsubscribe;
}, []);
最后,我们更新状态并呈现消息。
useEffect(() => {
// Subscribe to query with onSnapshot
const unsubscribe = query.onSnapshot(querySnapshot => {
// Get all documents from collection - with IDs
const data = querySnapshot.docs.map(doc => ({
...doc.data(),
id: doc.id,
}));
// Update state
setDocs(data);
});
// Detach listener
return unsubscribe;
}, []);
因此,如果我们在 Firestore 集合中创建一条新消息,我们应该会看到它自动出现在屏幕上。
// Channel.js
const Channel = ({ user = null }) => {
const [messages, setMessages] = useState([]);
useEffect(() => { ... });
return (
<ul>
{messages.map(message => (
<li key={message.id}>{message.text}</li>
))}
</ul>
);
};
// App.js
function App() {
...
return (
<div>
{
user ? (
<>
<Button onClick={signOut}>Sign out</Button>
<Channel user={user} />
</>
) : <Button onClick={signInWithGoogle}>Sign in with Google</Button>
}
</div>
);
}
将数据添加到 Firestore
好了!现在让我们添加从应用程序创建新消息的功能。
为了实现这一点,我们可以在组件内创建一个带有单个输入字段的表单Channel
。
const Channel = ({ user = null }) => {
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
...
const handleOnChange = e => {
setNewMessage(e.target.value);
};
return (
<>
<ul>
{messages.map(message => (
<li key={message.id}>{message.text}</li>
))}
</ul>
<form
onSubmit={handleOnSubmit}>
<input
ref={inputRef}
type="text"
value={newMessage}
onChange={handleOnChange}
placeholder="Type your message here..."
/>
<button
type="submit"
disabled={!newMessage}
>
Send
</button>
</form>
</>
);
};
并实现onSubmit
事件处理程序以将新消息添加到我们的 Firestore 数据库。
其实很简单,因为我们已经准备好了一切。我们再次使用 Firebase SDK 中的消息集合引用,并通过传入一个表示新消息数据的对象来调用 add 方法。
const handleOnSubmit = e => {
e.preventDefault();
const trimmedMessage = newMessage.trim();
if (trimmedMessage) {
// Add new message in Firestore
messagesRef.add({
text: trimmedMessage,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
uid,
displayName,
photoURL,
});
// Clear input field
setNewMessage('');
}
};
因此,我们有了文本本身、createdAt
时间戳(这里,我们让 Firebase 使用服务器生成的时间戳来设置值),然后我们有了用户 ID、用户displayName
及其photoURL
。我们通过 props 从父组件获取这些信息。
const { uid, displayName, photoURL } = user;
好了!现在我们的用户可以发送消息,其他人也可以实时阅读。
还有一件事,既然我们将用户信息附加到每条消息中,那么让我们在聊天中的每条消息旁边添加用户的照片和姓名。
为此,我们可以创建一个最后一个组件,用于Message
从组件内部呈现每个单独的消息Channel
。
// Message.js
const Message = ({
createdAt = null,
text = '',
displayName = '',
photoURL = '',
}) => {
if (!text) return null;
return <div></div>;
};
// Channel.js
const Channel = ({ user = null }) => {
...
return (
<>
<ul>
{messages.map(message => (
<li key={message.id}>
<Message {...message} />
</li>
))}
</ul>
</>
);
};
该组件负责格式化和渲染消息数据。因此,我们拥有用户的照片和显示名称,以及创建日期(我们使用date-fns
库将其转换为更易读的格式),最后,我们得到了消息的文本。
import { formatRelative } from 'date-fns';
const formatDate = date => {
let formattedDate = '';
if (date) {
// Convert the date in words relative to the current date
formattedDate = formatRelative(date, new Date());
// Uppercase the first letter
formattedDate =
formattedDate.charAt(0).toUpperCase() + formattedDate.slice(1);
}
return formattedDate;
};
const Message = ({
createdAt = null,
text = '',
displayName = '',
photoURL = '',
}) => {
if (!text) return null;
return (
<div>
{photoURL ? (
<img
src={photoURL}
alt="Avatar"
className="rounded-full mr-4"
width={45}
height={45}
/>
) : null}
{displayName ? <p>{displayName}</p> : null}
{createdAt?.seconds ? (
<span>{formatDate(new Date(createdAt.seconds * 1000))}</span>
) : null}
<p>{text}</p>
</div>
);
};
现在您拥有一个使用 React 和 Firebase 构建的强大的实时聊天应用程序!
我知道你现在怎么想!这个应用看起来糟透了。我们的用户根本用不上它。那就让我们用 CSS 魔法棒,把这个糟糕的应用改造成这样吧。
好多了对吧?
这不是关于 CSS 的视频/文章,所以我将节省您的时间和精力,并在此处链接源代码,以便您可以浏览它。
我甚至不再为我的 React 应用使用和编写纯 CSS 了。事实上,我正在使用Tailwind CSS快速为我的应用添加样式。它是那种一旦开始使用就停不下来的工具。我保证 Tailwind CSS 会彻底改变你的生活。
如果您希望我制作有关它的视频或教程,请在下面的评论部分告诉我。
并查看Github 存储库中我们刚刚构建的应用程序的源代码。
奖金
要了解如何通过 3 个简单的步骤免费部署此应用程序,请观看 YouTube 上的视频,并观看我完成这些步骤。
下一步是什么?
现在我邀请您更进一步并为其添加更多功能。
例如,您可以实施由人工智能驱动的审核策略,并将其包含在您的应用程序中,以自动审核用户的消息并禁止那些违反社区准则的消息。
例如,您还可以使用 Stripe 等工具向您的应用程序添加付款。
你明白我的意思了。可能性无穷无尽。这个应用程序只是一个起点。获取源代码,然后以此为基础构建你自己的应用程序。
结论
好了!就这样吧,各位。非常感谢你们观看完这段视频(或阅读文章)。
我真心希望这段视频对你们有用。请分享给你的朋友、同事,或者任何对使用 React 构建应用程序感兴趣的人。
如果您还没有订阅,请不要忘记在 Youtube 上订阅 AlterClass,点击“赞”按钮,并按响铃铛,以便每次我发布新视频时收到通知。
谢谢你!
成为一名 React 开发者
如果您需要了解有关使用 React 构建现代 Web 应用程序的更多信息,请查看我在 AlterClass.io 上的课程。
我的课程将教您掌握 React、成为一名成功的 React 开发人员并获得聘用所需的一切!
我将教授您使用 React 所需的所有概念,您将通过测验和编程评估获得大量的实践练习,并且您将自己构建真实世界的项目。
此外,您将成为不断壮大的学习者社区的一部分。
因此,请访问 AlterClass.io,注册我的课程,并开始构建强大的 React 应用程序的惊人组合。
文章来源:https://dev.to/alterclass/build-a-realtime-chat-app-in-5-min-with-react-and-firebase-3f8m