如何使用 React.js 构建实时电影投票系统
我们将要建造什么?
首先,我们将尝试构建一个简单的(实际上并不那么简单)电影评论应用。用户可以在其中对电影进行投票、发表评论,甚至可以添加电影。
然后,在第二部分中,我们可以更深入地为电影添加一个预订系统,并拥有多个影院。我们将使用Rocketgraph来实现这一点,因为它提供了一个完整的后端,带有身份验证和数据库,所以我们不必担心这一点。
为此,您需要定义用户、电影和存储它们的位置:
-
身份验证:您需要将用户存储在 Postgres DB 的表中。
-
实时:您需要从数据库直接获取实时评论和点赞,并将其发送到前端供用户使用。
-
数据库:您可以在其中将用户映射到电影,将用户和电影映射到喜欢。
💡要阅读本文,您需要对 React.js 有基本的了解
Rocketgraph:一个超越 Firebase 的完整后端,并且是开源的
简单介绍一下背景。Rocketgraph 提供了完整的后端。它配备了 Postgres 数据库、Hasura 控制台(用于管理 Postgres 并为数据添加 GraphQL 层)、身份验证和无服务器功能。
总而言之,我们为您的 Web 应用提供身份验证,为消息/通知/评论等实时事务提供 GraphQL,并为您想要卸载的任何内容提供无服务器函数。我们的无服务器 Github 应用可将您的 Github 代码自动编译为 AWS Lambda 函数。
那么 GraphQL 到底是什么?
GraphQL 是 Meta 制定的一种语言规范,它通过精确请求所需内容来实现实时数据查询。这与传统的 API 方法不同,传统的 API 方法将查询代码写入后端,而前端几乎无法控制请求的数据内容和方式。
可以把它想象成一个 JSON 查询。你在类似 JSON 的查询中请求你想要的数据,它会准确返回这些字段。
在本文中,我们将利用 GraphQL、React Apollo 和 Hasura 的强大功能,构建一个用于电影评分和评论的实时系统。我们也可以使用这个系统来预订电影票。
TLDR 版本
如果您只想查看代码,这里是本文的代码库。您可以在这里查看更多示例。这是Rocketgraph 背后的开源软件。
继续阅读
太棒了,让我们从基础开始
创建一个 React 项目并开发前端。后端暂时先不用管,我们稍后再添加。
mkdir movie-voting
cd movie-voting
接下来搭建一个基本的反应应用程序。
npx create-react-app ./
安装React Router以便能够在页面之间导航。安装 React-apollo 和 graphql 以实现如上所述的实时功能。
yarn add react-router-dom
yarn add @apollo/client graphql
删除项目里的logo等冗余文件,并更新index.js文件如下:
// src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<Router>
<Routes>
<Route path="/login" />
<Route path="/signup" />
<Route path="/" element={<App />} />
</Routes>
</Router>
</React.StrictMode>,
document.getElementById("root")
);
现在我们需要添加 App.js 和登录/注册组件。很简单。
App.js
import logo from './logo.svg';
import './App.css';
const movies = [
{
name: 'Snatch',
img: 'https://occ-0-3934-3211.1.nflxso.net/dnm/api/v6/E8vDc_W8CLv7-yMQu8KMEC7Rrr8/AAAABVJgO06RKuruJpcyezdM43Ai2ZjvNDmtbnwUXVtvXVhhvpL0tvhr4s9e3j8UojFCLao5a7v8Dg5kti1vFKcA0ldZXWnnC03nBRIt.jpg?r=cbf',
likes: 10,
state: true,
}
];
function App() {
return (
<div className="App">
<header className="App-header">
<p>
Movies list
</p>
</header>
{
movies.map(movie => {
return (
<div className="movie-box">
<div className="movie-box-header">
</div>
<div className="movie-box-body">
<img alt={movie.name} className="movie-image" src={movie.img} />
</div>
<div className="movie-box-footer">
{movie.name}
<div className="like-button"><i class="fa fa-heart" style={{"color": "red"}}aria-hidden="true"></i></div>
</div>
</div>
)
})
}
</div>
);
}
export default App;
现在我们已经完成了基本的主页设计。接下来我们来创建登录和注册页面。
signup.js
import React, { useState } from "react";
import { useNavigate } from 'react-router-dom';
export default function Login(props) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
navigate("/");
}
return (
<div>
<h1>Signup</h1>
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button>Signup</button>
</form>
</div>
);
}
让我们看看是否有效
yarn start
恭喜🥂,您刚刚创建了 Web 应用程序的框架。现在我们只需填写数据、身份验证和实时信息。
进入Rocketgraph。如何创建具有身份验证和无服务器功能的后端。
只需注册并单击仪表板中的创建项目:
接下来我们将了解一些令人惊叹的功能,这些功能将利用 GraphQL 的强大功能为您神奇地构建后端。
项目启动后,您将获得一个 Hasura 控制台和一个 Postgres 数据库,如下所示。请等待服务启动。这可能需要大约 3-5 分钟。
Hasura 是什么?
Hasura 是一款出色的开源工具,它可以将你的 Postgres 数据库转换为 GraphQL 格式。这意味着你的数据仍然在 Postgres 数据库中,但你却能享受 GraphQL 的强大功能。它还包含一个编辑器,可以根据你的 Postgres 表自动生成 GraphQL 查询。
返回 Rocketgraph
当您的项目启动时,您会在这里获得一个 Hasura 链接:
打开 Hasura,现在我们可以开始为我们的数据库创建表。
我们需要一个电影表,如下所示:
我们还需要让用户访问它。Rocketgraph 中user
有一个经过身份验证的角色,我们的 JS SDK 会将 JWT 随您的请求一起发送,这样您就无需亲自操作了。
转到电影的权限选项卡并添加以下权限:
对于插入放置权限如下:
对于选择来说,也是一样的:
使用 react-apollo 和 graphql 包来开启 GraphQL 之旅。Apollo 让你能够更轻松地直接从 React 查询 GraphQL,并提供了一些强大的功能useSubscription
,我们将在稍后讨论。
让我们安装它们。
yarn add @apollo/client graphql
我们还需要一些定制的 JS 库来使身份验证正常工作。
yarn add @rocketgraphql/react-apollo @rocketgraphql/rocketgraph-js-sdk
现在我们将使用RApolloProvider
提供的@rocketgraphql/react-apollo
首先创建一个名为的文件夹utils
,然后config.js
在其中创建以下内容:
import { createClient } from "@rocketgraphql/rocketgraph-js-sdk";
import Cookies from 'js-cookie';
const config = {
baseURL: "https://backend-REPLACE",
};
const { auth } = createClient(config);
export { auth };
将上述内容替换https://backend-REPLACE
为 Rocketgraph 仪表板中的后端 URL:
您会在Auth
参考资料 部分找到它。
将代码更改为index.js
:
// src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import App from "./App";
import Signup from "./components/login";
import { RApolloProvider } from "@rocketgraphql/react-apollo";
import { auth } from "./utils/config";
ReactDOM.render(
<React.StrictMode>
<RApolloProvider auth={auth} gqlEndpoint="https://gqlEndpoint/v1/graphql">
<Router>
<Routes>
<Route path="/login" element={<Signup />}/>
<Route path="/signup" />
<Route path="/" element={<App />} />
</Routes>
</Router>
</RApolloProvider>
</React.StrictMode>,
document.getElementById("root")
);
将上述内容更改https://gqlEndpoint/v1/graphql
为 Hasura 控制台中的 graphql 端点:
接下来我们将添加授权。
login.js
import React, { useState } from "react";
import { useNavigate } from 'react-router-dom';
import { auth } from "../utils/config";
export default function Login(props) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
// login
try {
await auth.signIn({email, password, provider: "local"});
} catch (error) {
alert("error logging in");
console.error(error);
return;
}
navigate("/");
}
return (
<div>
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button>Login</button>
</form>
</div>
);
}
signup.js
import React, { useState } from "react";
import { useNavigate } from 'react-router-dom';
import { auth } from "../utils/config";
export default function Login(props) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
// login
try {
await auth.register({email, password});
} catch (error) {
alert("error logging in");
console.error(error);
return;
}
navigate("/");
}
return (
<div>
<h1>Signup</h1>
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button>Login</button>
</form>
</div>
);
}
就这样。Rocketgraph 会处理剩下的事情。用户信息将被填充到用户数据库中。
您可以通过注册并检查用户是否已创建来测试这一点。
让我们构建更多功能
输入 react-apollo。
App.js
import './App.css';
import { gql, useSubscription } from "@apollo/client";
const GET_MOVIES = gql`
subscription {
movies {
id
created_at
name
image
}
}
`;
function App() {
const { data, loading } = useSubscription(GET_MOVIES);
if (loading) {
return <div>Loading</div>;
}
return (
<div className="App">
<header className="App-header">
<p>
Movies list
</p>
</header>
{
data && data.movies && data.movies.length ?
data.movies.map((movie, index) => {
return (
<div className="movie-box" key={index}>
<div className="movie-box-header">
</div>
<div className="movie-box-body">
<img alt={movie.name} className="movie-image" src={movie.image} />
</div>
<div className="movie-box-footer">
{movie.name}
<div className="like-button"><i className="fa fa-heart" style={{"color": "red"}} aria-hidden="true"></i></div>
</div>
</div>
)
}) : "No movies"
}
</div>
);
}
export default App;
就是这样,只需在您的数据库中添加记录,您就可以在这里实时看到它。
太棒了😎 现在我们终于可以添加点赞按钮了
重要部分(用户 ID)
首先在 Hasura 中创建带有 id、movie_id 和 user_id 的 likes 表,如下所示
我们必须从 JWT 令牌本身中提取这个 User-Id。
为此,
步骤 1
创建一个名为的新角色user
并单击Insert
以编辑其权限
第 2 步
允许用户角色修改所有内容。勾选以下复选框
第 3 步 - 最重要的
自动设置用户 ID
点击列预设并选择用户 ID。从 X-Hasura-user-id 设置。
然后点击“保存”。现在我们已经准备好了表格来保存点赞/投票。
输入聚合(点赞)
components
在named中创建新文件likeCount.js
import React, { useState } from "react";
import { gql, useSubscription, useMutation } from "@apollo/client";
const likes = (movie_id) => gql`
subscription {
likes(where: {movie_id: {_eq: "${movie_id}"}}) {
id
user_id
}
}
`;
const LIKE = gql`
mutation like($movie_id: uuid!) {
insert_likes(objects: {movie_id: $movie_id}) {
affected_rows
}
}
`;
const UNLIKE = gql`
mutation unlike($movie_id: uuid!) {
delete_likes(where: {movie_id: {_eq: $movie_id}}) {
affected_rows
}
}
`;
function Component({movie}) {
const LIKE_COUNT = likes(movie.id);
const [addLike, { like_data, like_loading, error }] = useMutation(LIKE);
const [unLike, _] = useMutation(UNLIKE);
const [isRed, setIsRed] = useState(false);
const { data, loading } = useSubscription(LIKE_COUNT);
console.log(data, movie);
if (loading) {
return <div>Loading</div>;
}
const likeThis = () => {
setIsRed(!isRed);
if (isRed) {
unLike({variables: {movie_id: movie.id}});
} else {
addLike({ variables: { movie_id: movie.id }});
}
}
return (
<span>
{data.likes.length}
<i className="fa fa-heart" style={{"color": isRed ? "red" : "gray"}} aria-hidden="true" onClick={likeThis}></i>
</span>
);
}
export default Component;
哦,等等!这会删除表中的所有“赞”。所以,让我们通过以下方式保护表:
并将其导入App.js
如下:
import './App.css';
import { gql, useSubscription } from "@apollo/client";
import LikeCountComponent from "./components/likeCount";
const GET_MOVIES = gql`
subscription {
movies {
id
created_at
name
image
}
}
`;
function App() {
const { data, loading } = useSubscription(GET_MOVIES);
if (loading) {
return <div>Loading</div>;
}
return (
<div className="App">
<header className="App-header">
<p>
Movies list
</p>
</header>
{
data && data.movies && data.movies.length ?
data.movies.map((movie, index) => {
return (
<div className="movie-box" key={index}>
<div className="movie-box-header">
</div>
<div className="movie-box-body">
<img alt={movie.name} className="movie-image" src={movie.image} />
</div>
<div className="movie-box-footer">
{movie.name}
<div className="like-button"><LikeCountComponent movie={movie} /></div>
</div>
</div>
)
}) : "No movies"
}
</div>
);
}
export default App;
好了!恭喜,你成功制作了一个电影投票应用。
文章来源:https://dev.to/kaushik94/how-to-build-a-real-time-movie-voting-system-using-reactjs-3nfa