使用数据集时,重要的第一步是找到一个视图,使您可以一目了然地查看所需的所有相关信息,无论是通过列出项目,将它们分组为表中的列,还是正如我将在本文中证明的那样,您可以根据其状态无缝移动项目。
现在将XATA与React集成以创建具有以下功能的板:
- 持久数据
- 创建,阅读,更新和删除资产
- 设置资产的可用性,即,如果玩家受伤或暂停
- 单击并删除功能以管理球员的状态与您的团队有关
我们将使用以下工具:
- Xata
- React
- Vite
- 云
XATA是一项数据库服务,可提供内置分析,搜索引擎和易于使用的电子表格式用户界面,以及其他出色的功能。
React是一个JavaScript库,用于构建用户界面,并具有围绕其构建的重要工具生态系统。
vite是一种构建工具,旨在为当前的Web项目提供更快,更精美的开发体验。
Cloudinary提供基于云的图像和视频管理服务,包括存储和交付,我们现在将利用这些服务将幻想徽标添加到我们的应用中。
对于那些想知道的人,FPL(幻想英超联赛)是一款幻想足球比赛,全球拥有超过1000万球员。
这是一个完整的应用程序:
创建XATA数据库
要开始使用XATA,请创建一个帐户here并按照docs中提供的步骤。
登录工作空间,您应该看到以下屏幕。
创建数据库并使用以下模式创建表:
资产表是用于构建此应用程序的唯一使用的表,我将仔细研究我在数据库上设置的详细信息和约束。有四列可以存储每个记录的关联值,所有列类型都是字符串。
id
字段默认情况下包含一个自动生成的值。 name
字段设置为唯一,因此没有两个资产可以具有相同的值,因为它在开发UI时用于过滤值。需要status
字段,因此如果尝试在不设置状态的情况下创建记录,则会丢弃错误。
上图显示了创建新记录所需的形式,而不是ORM或RAWSQLðä。
现在已经处理了所有数据库要求,我们将继续构建前端接口并使用SDK将其连接到XATA。
构建React前端
我为前端选择了反应[最受欢迎的UI库]就不足为奇了。另一方面,我将vite
作为我的构建工具。它在VUE社区中更广泛地使用,但是它提供了来自其他前端框架的模板,并且效果非常好。让我们看看它的行动:
#scaffold the project you can replace `fpl` with a preferred project name
yarn create vite fpl --template react
#change directories to the newly created project
cd fpl
#run yarn to install packages
yarn
就是这样。您可以自由地将其与您选择的其他任何脚手架框架交换
要在应用程序中添加XATA,请从安装全球CLI
npm i -g @xata.io/cli
按照指示here创建API键并初始化您的项目。
初始化后,将xata init
生成的src
目录中的文件移动到您的React Apps src
文件夹中。
现在,将XATA客户端发生器导入您的App
.
jsx
,调用并导出它,以便其他组件可以访问它。
import { useEffect, useState } from "react";
import { getXataClient } from "./xata";
import "./App.css";
export const xata = getXataClient();
完成此操作后,我们准备构建应用程序的前端。尽管现在是提及XATA更适合具有服务器端功能的框架的好时机,例如Next,Nuxt和Svelte套件。
与浏览器一起使用XATA时,有可能暴露API密钥,因此,如果您尝试在浏览器中运行它,您将在控制台中看到此消息:
Uncaught Error: You are trying to use Xata from the browser, which is potentially a non-secure environment. If you understand the security concerns, such as leaking your credentials, pass `enableBrowser: true` to the client options to remove this error.
通过修改defaultOptions
对象在xata.js
:
中很容易修复。
// Generated by Xata Codegen 0.18.0. Please do not edit.
import { buildClient } from "@xata.io/client";
/** @typedef { import('./types').SchemaTables } SchemaTables */
/** @type { SchemaTables } */
const tables = [
{
name: "Assets",
columns: [
{ name: "name", type: "string", unique: true },
{ name: "status", type: "string", notNull: true, defaultValue: "in" },
{ name: "availability", type: "string" },
],
},
];
/** @type { import('@xata.io/client').ClientConstructor<{}> } */
const DatabaseClient = buildClient();
const defaultOptions = {
databaseURL:
"https://DAMILARE-s-workspace-uaav04.us-east-1.xata.sh/db/hackathon",
};
/** @typedef { import('./types').DatabaseSchema } DatabaseSchema */
/** @extends DatabaseClient<DatabaseSchema> */
export class XataClient extends DatabaseClient {
constructor(options) {
super({ ...defaultOptions, ...options }, tables);
}
}
let instance = undefined;
/** @type { () => XataClient } */
export const getXataClient = () => {
if (instance) return instance;
instance = new XataClient();
return instance;
};
将默认选项对象更改为:
const defaultOptions = {
databaseURL:
"https://DAMILARE-s-workspace-uaav04.us-east-1.xata.sh/db/hackathon",
enableBrowser: true,
};
现在添加CSS,打开App.css
文件并添加以下规则集:
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.header {
display: flex;
gap: 1em;
justify-content: center;
align-items: center;
}
.index {
display: grid;
height: 100vh;
grid-template-rows: auto 1fr auto;
row-gap: .4em;
}
.board {
display: grid;
grid-template-columns: repeat(3, 1fr);
column-gap: .2em;
}
.assets {
display: grid;
grid-template-rows: repeat(11, 1fr);
background-color: rgb(250, 189, 189);
}
.asset-card {
background-color: rgba(255, 255, 255, 0.925);
border: solid 1px grey;
padding: .8em;
margin-inline: .4em;
display: flex;
justify-content: space-between;
align-items: center;
}
.avail-selector {
appearance: none;
background-color: #02bafe;
color: white;
border-radius: 3.2em;
height: 2em;
margin-left: 0;
width: 8em;
text-align: center;
border: none;
}
.suspended {
background-color: rgb(156, 86, 226);
}
.injured {
background-color: rgb(158, 158, 8);
}
.fa-trash {
color: rgb(250, 66, 66);
}
.hide {
display: none;
}
.add-asset-form {
display: flex;
flex-direction: column;
gap: 2px;
}
并在index.html
中包含以下链接以在此项目中使用字体真棒图标:
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
现在我们准备做出反应! ð
组件
本节将显示用于构建应用程序及其功能的组件。
使用以下成员创建一个components
目录并插入以下代码:
- Assetlist.jsx
- addassetform.jsx
- AssetDetail.jsx
AssetDetail.jsx
import React, { useState } from "react";
import { xata } from "../App";
function AssetDetail({ asset, deleteAsset, onClick }) {
const optionsAvailable = ["fit", "injured", "suspended"];
const [availability, setAvailability] = useState(asset.availability);
const { id } = asset;
const handleChange = (e) => {
let availability = e.target.value;
xata.db.Assets.update({ availability, id });
setAvailability(availability);
};
return (
<p className="asset-card" key={asset.id} onClick={() => onClick(asset)}>
<span>{asset.name}</span>{" "}
<select
name="availability"
onChange={handleChange}
className={`avail-selector ${availability}`}
>
<option value={asset.availability}>{asset.availability}</option>
{optionsAvailable.map(
(option) =>
option != asset.availability && (
<option value={option} key={option}>
{option}
</option>
)
)}
</select>
<i
className="fa-solid fa-trash"
onClick={(e) => {
deleteAsset(asset.name, asset.id);
e.stopPropagation();
}}
/>
</p>
);
}
export default AssetDetail;
此组件显示一张带有有关单个资产(即名称和可用性)的详细信息的卡。您可以在3个不同的选项之间选择,并根据首选选项进行更改。
它还包含带有stopPropagation
集的onClick
事件中的删除功能,因此不会干扰点击功能。调用数据库以删除和更新资产的可用性。
AddAssetForm.jsx
import React from "react";
import { useState } from "react";
import { xata } from "../App";
function AddAssetForm({ status, show, onFormClick, updateAssets }) {
const [name, setName] = useState("");
// const [status, setStatus] = useState(status);
const [availability, setAvail] = useState("");
const handleChange = (e) => setName(e.target.value);
const handleSubmit = (e) => {
e.preventDefault();
updateAssets({ name, availability, status });
xata.db.Assets.create({ name, availability, status });
setName("");
setAvail("");
onFormClick();
};
return (
<div>
<form
action="post"
onSubmit={handleSubmit}
className={!show ? "hide" : "add-asset-form"}
>
Add Asset
<input type="text" value={name} onChange={handleChange} />
<input
type="text"
name="availability"
value={availability}
onChange={(e) => setAvail(e.target.value)}
/>
<input type="submit" value="add player" />
</form>
</div>
);
}
export default AddAssetForm;
此组件包含将新资产添加到列表中的表单。我有一个onFormClick
道具,可以在提交时切换其可见性。提交后,调用数据库,创建新资产,并更新本地状态以显示更改。
AssetList.jsx
import { useState } from "react";
import { xata } from "../App";
import AddAssetForm from "./AddAssetForm";
import AssetDetail from "./AssetDetail";
export default function AssetList({
assets,
status,
updateAsset,
allAssets,
selectAssetCardOnClick,
moveCard,
}) {
const [showForm, setShowForm] = useState(false);
const updateAssetsDisplayed = (newAsset) => {
updateAsset(allAssets.concat(newAsset));
};
const handleClick = () => {
setShowForm(!showForm);
};
const deleteAsset = (assetName, assetID) => {
xata.db.Assets.delete(assetID);
updateAsset(allAssets.filter((asset) => asset.name !== assetName));
};
return (
<div className="assets" onClick={() => moveCard(status)}>
{assets.map((asset) => (
<AssetDetail
asset={asset}
allAssets={allAssets}
deleteAsset={deleteAsset}
onClick={selectAssetCardOnClick}
key={asset.id}
/>
))}
{assets.length < 11 ? (
<>
<AddAssetForm
status={status}
show={showForm}
onFormClick={handleClick}
updateAssets={updateAssetsDisplayed}
/>
<i className="fa-regular fa-plus" onClick={handleClick} />
</>
) : (
""
)}
</div>
);
}
此组件列出了已创建的特定状态的所有资产。它仅设置为允许用户切换表单以创建新资产,而其中包含的资产数量小于11。
。最后,将所有内容放在App.jsx
文件中:
import { useEffect, useState } from "react";
import { getXataClient } from "./xata";
import "./App.css";
import AssetList from "./components/AssetList";
export const xata = getXataClient();
function App() {
const [assets, setAssets] = useState([]);
const [selectedCard, setSelectedCard] = useState(null);
useEffect(() => {
(async () => {
const data = await xata.db.Assets.getAll();
setAssets(data);
})();
}, []);
const handleCardSelect = (currentCardName) => {
setSelectedCard(currentCardName);
};
const moveCard = (newStatus) => {
try {
const { id } = selectedCard;
if (selectedCard) {
xata.db.Assets.update({ id, status: newStatus }),
setAssets((prevState) => [
...prevState.filter((asset) => asset.name !== selectedCard.name),
{ ...selectedCard, status: newStatus },
]);
setSelectedCard(null);
}
} catch (e) {}
};
const assetsIn = assets.filter(
(asset) => asset.status.toLowerCase() === "in"
);
const assetsOut = assets.filter(
(asset) => asset.status.toLowerCase() === "out"
);
const assetsWatch = assets.filter(
(asset) => asset.status.toLowerCase() === "watch"
);
return (
<div className="index">
<header className="header">FPL Assets</header>
<div className="board">
<span>
<h3>In</h3>
<AssetList
assets={assetsIn}
allAssets={assets}
status="In"
updateAsset={setAssets}
selectAssetCardOnClick={handleCardSelect}
moveCard={moveCard}
/>
</span>
<span>
<h3>Watching</h3>
<AssetList
assets={assetsWatch}
allAssets={assets}
status="Watch"
selectAssetCardOnClick={handleCardSelect}
updateAsset={setAssets}
moveCard={moveCard}
/>
</span>
<span>
<h3>Out</h3>
<AssetList
assets={assetsOut}
allAssets={assets}
status="Out"
updateAsset={setAssets}
selectAssetCardOnClick={handleCardSelect}
moveCard={moveCard}
/>
</span>
</div>
<footer>and some bottom stuff</footer>
</div>
);
}
export default App;
在App root中,我们使用useEffect
获取数据库中的初始数据,通过其当前状态过滤卡并根据其当前状态将其提供给AssetList
组件。
最后,这包含处理单击功能的moveCard
功能,以在不同列表之间移动资产。
带有云的图像存储
Cloudinary提供基于云的图像和视频管理服务,包括存储和交付,我们现在将利用这些服务将幻想徽标添加到我们的应用中。
首先创建一个帐户,registering,登录,访问媒体库和上传徽标,您可以找到here。
要使它与React一起使用,您必须安装Cloudinary的React库:
yarn add @cloudinary/url-gen @cloudinary/react
现在将以下代码添加到App.jsx
import { Cloudinary } from "@cloudinary/url-gen";
import { AdvancedImage } from "@cloudinary/react";
import { fill } from "@cloudinary/url-gen/actions/resize";
function App() {
...
const cld = new Cloudinary({
cloud: {
cloudName: <CLOUD_NAME>,
},
});
const Image = cld
.image("FPL-2223-EDITORIAL-STATEMENT_2_kd8sgw.png")
.resize(fill().width(200).height(50));
...
用cloud_name替换,在您创建帐户时会自动分配。访问Cloudinary的动态URL基于您的Cloud_name。
之后,在应用程序标题中包括新创建的图像,然后完成。
return(
...
<header className="header">
<span>
<AdvancedImage cldImg={Image} />
</span>
<span>
<h2>Assets</h2>
</span>
</header>
...)
这仅刮擦云的表面。从快速内容交付网络到基于面部检测的自动转换,同时为每种广泛使用的编程语言提供易于使用的API。对于需要任何形式的图像或视频处理的各种项目的项目,这是一项方便的服务。
结论
您可以找到所有的code;我的应用程序是here。例如,您可以通过像官方的Xata tutorial中的服务器端框架中复制本文中提出的想法。
。