简介
React是创建有状态界面的最佳库之一,也是我们所知的互联网的重要组成部分。
包括Twitter、Facebook、Instagram和Airbnb在内的许多网络应用都依赖于这个库,以向数十亿 的用户提供有状态的、跨平台的应用。它仍然是同类产品中最受欢迎的库之一。
React Hooks--改变游戏规则
React 16.8引入了一种新的开发模式,称为钩子。这个新功能把库带到了一个全新的地方,使人们比以往任何时候都更容易在函数的背景下编写和理解组件,而不是类。
看看这个Gif,从开发者的角度来看,这些功能组件的效率会提高很多:

推特。Pavel @prchdk
React中有各种类型的钩子,这是有原因的。不同的任务需要不同的钩子,从存储变量到备忘函数。
许多钩子的特点是所谓的依赖阵列。钩子观察数组中变量的变化,如果观察到任何变化,它就会重新运行。
以useEffect 钩子为例。这个钩子在一个组件第一次安装时运行,并且在这个依赖性数组中的有状态变量发生变化时运行:
const [count, setCount] = useState(0);
useEffect(() => {
console.log(count);
}, [count]);
在上面的代码片段中,count 将在两种情况下被记录:
- 当组件首次挂载时
- 当你使用
setCount来改变 "D "的值时。count
尽管还有很多关于钩子的内容需要深入研究,但理解上面的概念对本文的其余部分至关重要。这是因为在这篇文章中,我们将演示你如何使用这个依赖数组来自动重新获取你的中心数据,类似于它重新运行useEffect 钩子。
数据库设置
几乎每一个React应用程序的生产实例都会在某些时候使用数据库,无论是用于存储用户信息、业务信息还是API数据。
在你的React或React Native应用程序中实现数据库有很多方法,但有一些特定的方法与React编程模式,特别是钩子,结合得非常好。
在为你的React应用提供的各种托管解决方案中,你会发现使用无服务器架构的好处最大。我可以写出无服务器架构的所有好处,但这也可能是一篇独立的文章。这里只列举几个:
- 按需自动扩展
- 超级容易部署
- 忘记服务器的管理和维护
- 有更多时间用于UI/UX
- 成本开销降至0
下面演示的方法结合了无服务器应用开发的所有好处,以及与React编程生态系统完美配合的定制钩子。
对于那些想知道的人来说,下面介绍的库也能与React Native一起使用,适合移动优先的开发者。
我们最终会有一个名为useReturn 的钩子,它将始终返回一个给定查询的新实例。它看起来会像下面这样:
const [minRating, setMinRating] = useState(0);
const { frame } = useReturn(() => /* Your query */, [minRating])
return <div>{frame.map(ele => <Card {...ele} />)}</div>
不要担心现在这个不连贯。几分钟后你就能完美地适应你的用例。
请注意,在这个例子中,frame 实例,也就是你数据库中的记录数组,将在两种情况下被更新:
minRating(或依赖数组中的任何东西)变化- 数据库的另一个实例 (
db) 创建、更新或删除了数据
React设置
本节将简要地演示如何创建一个React项目。如果你已经很熟悉了,可以随意跳到下一部分。
React的开发团队创建了一个易于使用的脚本,名为create-react-app 。唯一的前提是你的机器已经安装了node 和npm ,无论如何你都会需要。
所以,如果你还没有这些软件包,请按照这里的说明快速安装。
在你想放置新项目的目录中打开命令提示符或终端,运行以下命令:
# npx create-react-app serverless-app
该过程完成后,进入serverless-app 目录,像这样启动该项目:
# cd serverless-app
# npm run start
这将创建一个你的应用程序的本地实例,当你位于src/ 文件夹中的文件被编辑时,它会自动重新加载(称为热加载)。一个浏览器窗口应该自动弹出。如果没有,请打开你的网络浏览器,并进入http://localhost:3000 。

Easybase设置
让我们通过进入我们的项目目录并执行npm install easybase-react 来安装一个叫做easybase-react 的库。这是我们在这个演示中唯一需要的依赖。
接下来,在easybase.io创建一个账户(你可以使用免费层)。
一旦你登录,使用"+创建"按钮来创建一个新表。让我们把它命名为MY TABLE,并给它三列:评级(数字),海报(图片),和标题(字符串):


点击下一步,完成你的下一个表的创建。它将自动弹出,但你也可以展开左侧抽屉中的表按钮,在那里选择它:

为了演示的目的,让我们添加一个例子行,这样我们就可以在我们的React应用程序中显示它。使用表格左上方的**'+**'按钮来添加一个新行:

我的例子将以电影数据为特色,但请自由使用最适合你的应用程序的任何类型的数据。
在我们回到代码之前的最后一步是在Easybase界面创建一个新的项目 。这将给我们一个配置文件,使我们的应用程序可以安全地访问数据库。保持这个配置文件的私密性,因为它包含可以用来访问你的数据的凭证。
在左边的抽屉里,前往"项目>创建项目":

转到权限。点击你的表的名字并启用"未登录用户>读、写":

不要忘记点击'保存'。
最后,转到项目令牌标签,下载你的自定义配置令牌:

把这个令牌放在你的React项目中的App.js 旁边,这样结构看起来就像下面这样:
├ ...
├ ebconfig.js
├ App.css
├ App.js
├ index.js
└ ...
现在让我们回到代码上。在你的React项目中打开src/index.js 文件。首先,从我们之前安装的easybase-react 包中导入EasybaseProvider 和 我们自定义的ebconfig.js token。然后,用<EasybaseProvider ebconfig={ebconfig} > 包裹<App /> :
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import ebconfig from './ebconfig'; // Add this
import { EasybaseProvider } from 'easybase-react'; // Add this
ReactDOM.render(
<React.StrictMode>
<EasybaseProvider ebconfig={ebconfig}> {/* <-- */}
<App />
</EasybaseProvider> {/* <-- */}
</React.StrictMode>,
document.getElementById('root')
);
不要忘记在ebconfig道具中传递你的配置文件
这种设置被称为提供者模式,使我们React项目中的所有组件都能有效地访问该提供者的上下文,它是在你的组件中组织状态的最佳方式(另外它是原生支持的)。在我们的例子中,那是一个叫做useEasybase 的钩子。
useEasybase钩子
在这一点上,项目配置已经完成。前往src/App.js ,删除导入和App 函数内的一切。
现在,让我们用easybase-react 包来设置我们的第一个组件。这个过程可以在你的项目中复制,适用于以下的任何属性 [useEasybase](https://easybase.io/docs/easybase-react/interfaces/types_types.contextvalue.html) (P.S. 有很多这样的属性)。
首先,从那个easybase-react 包中导入useEasybase 。让我们像这样抓取useReturn,db, 和e:
import { useEasybase } from 'easybase-react';
function App() {
const { useReturn, db, e } = useEasybase();
return (
);
}
export default App;
你可能想知道--这些函数是什么?
db - 正如其名称所述,这个函数让我们访问我们的数据库,它像这样工作:
let records = await db('MY TABLE').return().all()
这是一个非常简单的例子,但db 函数是相当强大的。在这里阅读更多关于它的信息。
e - 这代表了表达式。在 函数中使用它来建立查询,在 的 函数中使用db db .where eq (等于),neq (不等于),lt (小于),or (OR语句),以及更多的 e.eq("column_name", value) 的形式。 这将查询列_名称 等于什么值的记录。
现在我们可以使用表达式来做一个复合查询:
let records = await db('MY TABLE').return(e.avg('rating')).where(e.or(e.like('title', 'T%'), e.lt('rating', 80))).all();
// e.avg = Return the average of 'rating' where:
// e.or = OR statement on:
// e.like = string matching pattern [title starts with 'T']
// e.lt = less than [rating < 80]
useReturn - 最后,这就是前面提到的钩子。它通过包装 函数而工作。这个钩子会自动订阅 中的变化。最重要的是,它将使我们能够访问一个有状态的数据数组,称为 。db db frame
const { useReturn, db, e } = useEasybase();
const { frame } = useReturn(() => db().return()
.where(e.gt('rating', minRating)) // Where rating > minRating
.limit(limit), // Limit query length
[minRating, limit]); // Also returns some helpers:
// 'error' - any
// 'loading' - boolean
// 'manualFetch' - async function
// 'unsubscribe' - function
不要在useReturn 钩子中使用.all 或.one ,这会被自动处理。欲了解更多信息,请看这里的文档。
第一个组件
让我们在我们的空src/App.js 中使用这些函数,如下所示:
import { useEasybase } from "easybase-react";
function App() {
const { useReturn, db, e } = useEasybase();
const { frame } = useReturn(() => db("MY TABLE").return(), []);
return (
<div>{frame.map(ele => JSON.stringify(ele))}</div>
);
}
export default App;
作为一个示范,这将简单地显示当前表中的一条记录的字符串表示:

**恭喜你,你的数据库已经上线运行了。**现在,让我们实现一个自定义组件,叫做<Card /> ,它将在用户界面中给我们的记录提供一些结构(请随意将这些组件放在单独的文件中进行组织):
function Card({ rating, poster, title, _key }) {
const cardStyle = {
display: "inline-block",
margin: 10,
padding: 10,
borderRadius: 10,
background: "#eaeaea",
minWidth: 200,
};
return (
<div style={cardStyle}>
<img
src={poster}
style={{ height: 300, minWidth: 200 }}
/>
<h2>{title}</h2>
<h4>Rating: {rating}</h4>
</div>
);
}
function App() {
const { useReturn, db, e } = useEasybase();
const { frame } = useReturn(() => db("MY TABLE").return(), []);
return (
<div style={{ textAlign: "center", display: "inline-block" }}>
{frame.map(ele => <Card {...ele} />)}
</div>
);
}

这看起来好多了。为了简洁起见,我将保持我的造型简单。你可以自由地给这个项目赋予你自己的外观!
你可以看到,<Card /> 使用原始记录的所有属性作为它的道具,再加上一个叫做_key的道具。 _key是每个记录的唯一标识符,与其他属性一起返回。 这对于查询和更新特定记录非常有用。稍后会有更多介绍。
插入记录
现在,让我们快速实现一个向数据库添加新卡的方法。这也将展示useReturn 钩子如何在我们添加不同组件的记录时自动刷新。
在我们映射框架数组后,显示一个新的按钮:
// ...
function AddCardButton() {
const addCardStyle = {
background: "#ea55aa",
display: "inline-block",
width: 200,
borderRadius: 10,
cursor: "pointer",
};
return (
<div style={addCardStyle}>
<h2 style={{ color: "#fff" }}>Add Card</h2>
</div>
);
}
function App() {
const { useReturn, db, e } = useEasybase();
const { frame } = useReturn(() => db("MY TABLE").return(), []);
return (
<div style={{ textAlign: "center", display: "inline-block" }}>
{frame.map(ele => <Card {...ele} />)}
<AddCardButton /> {/* <- New button */}
</div>
);
}

在React或React Native应用程序中,有许多不同的方法来收集用户输入。在这个例子中,我将使用内置的 [prompt](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt)函数,但你也可以使用表单、对话框等。
一旦我们收集到了新的记录细节,就用db 函数上传它们。所以,让我们把那个 [useEasybase](https://easybase.io/docs/easybase-react/interfaces/types_types.contextvalue.html)钩子。而不是.return ,我们将使用 [.insert](https://easybase.github.io/EasyQB/docs/insert_queries.html#insert)(我们将在后面探讨上传图片)。
在代码中,这个的实现可以看起来像下面这样:
function AddCardButton() {
// ...
const { db } = useEasybase();
async function addCardClick() {
let title = prompt("Please enter a movie title");
let rating = prompt("Please enter the rating for this movie");
if (!rating || !title) {
return;
}
db("MY TABLE")
.insert({ title, rating: Number(rating) })
.one();
}
return (
<div style={addCardStyle} onClick={addCardClick}> {/* <- onClick */}
<h2 style={{ color: "#fff" }}>Add Card</h2>
</div>
);
}
点击这个新按钮并输入一些数值:


这就是了,新的记录!
最后,让我们用 [setImage](https://easybase.io/docs/easybase-react/interfaces/types_types.contextvalue.html#setimage)函数,从useEasybase 。媒体(图片、视频、文件)的处理方式与其他值不同,需要上传,而不是插入。
在这里,我们终于可以使用 _key 属性来唯一地识别当前记录。 该属性也常用于db.set ,db.delete ,等等。
当用户点击图片(或空的图片空间)时,他们将能够上传一个新的图片。useReturn 将再次展示它自动刷新新的数据。
回到<Card /> 组件,并带入那个useEasybase 钩子。使用一个隐藏的输入是一个常见的技巧,使一个图像也显示为一个文件输入:
function Card({ rating, poster, title, _key }) {
// ...
const { setImage } = useEasybase();
async function onFileChange(e) {
if (e.target.files[0]) {
await setImage(_key, "poster", e.target.files[0], "MY TABLE");
}
}
return (
<div style={cardStyle}>
<input id={"fileInput" + _key} hidden type="file" onChange={onFileChange} />
<img
src={poster}
style={{ height: 300, minWidth: 200 }}
onClick={_ => document.getElementById("fileInput" + _key).click()}
/>
<h2>{title}</h2>
<h4>Rating: {rating}</h4>
</div>
);
}
现在,点击一个<Card /> 的图片会出现一个文件选择器。使用该选择器从你的机器上上传一张图片:

这很有效!上传的图片将通过Easybase CDN提供,并附在你的记录上。frame 应该自动显示它。
注意,这些变化也反映在Easybase的网络应用中。

查询
让我们再添加一个组件来演示如何使用useReturn 钩子的依赖阵列。
作为一个示范,我将实现一个数字输入,当改变时,更新useReturn 钩子中使用的查询。
通常情况下,你会在db.where 函数中使用一个表达式来进行这些有状态的查询。下面是一个简单的例子,包裹了根<App /> ,并添加了一个受控输入。注意新的ratingMin变量。
import { useEasybase } from "easybase-react";
// ...
function App() {
const [ratingMin, setRatingMin] = useState(0); // <- for new input
const { useReturn, db, e } = useEasybase();
const { frame } = useReturn(() => db("MY TABLE").return(), []);
return (
<div>
<div style={{ textAlign: "center", display: "inline-block" }}>
{frame.map(ele => <Card {...ele} />)}
<AddCardButton />
</div>
<p>
Rating filter:
<input
type="number"
value={ratingMin} // controlled input
onChange={e => setRatingMin(Number(e.target.value))}
/>
</p>
</div>
);
}

剩下的就是在db 函数中使用ratingMin ,并把它放在依赖数组中。我们将使用e.gte('rating', ratingMin) 来查询 "评级"(列名)大于或等于ratingMin 的记录。
function App() {
const [ratingMin, setRatingMin] = useState(0); // <- for new input
const { useReturn, db, e } = useEasybase();
const { frame } = useReturn(
() => db("MY TABLE").return().where(e.gte("rating", ratingMin)),
[ratingMin]
);
// ...
}
就这样,你的frame 响应状态变化并相应地更新查询:

你可以根据你的需要添加尽可能多的记录。

所有这些变化都将与你的远程数据库同步。专业提示:使用 [.limit](https://easybase.github.io/EasyQB/docs/select_queries.html#limit)和 [.offset](https://easybase.github.io/EasyQB/docs/select_queries.html#offset)来实现分页,如果你有几千甚至几万条记录。
结论
该 easybase-react包有很多有用的功能,你可能会觉得很有帮助,特别是在用户认证和数据库方面。
如果你想看看这个库在React和React Native中的所有功能,可以看看这个演练。
这篇文章中的查询生成器的功能类似于Firebase数据库中使用的语法,而且相当灵活。例如,一个高级用例是用聚合器选择列,如 [e.min](https://easybase.github.io/EasyQB/docs/operations.html#minimum)和 [e.max](https://easybase.github.io/EasyQB/docs/operations.html#maximum).
此外,如果你的应用程序中有一些更复杂的业务逻辑,可以尝试使用 [dbEventListener](https://easybase.io/docs/easybase-react/interfaces/types_types.contextvalue.html#dbeventlistener)处理程序。这将在db 实例运行任何查询时运行一个回调函数。它也会从useEasybase 钩子中返回。
谢谢你的阅读!这是对React友好、有状态数据库钩子和无服务器编程的简单介绍,无服务器编程是个人和小型团队中流行的编程架构。
这种流行来自于没有传统的后台设置,而传统的后台设置有很多成本、时间和管理开销。
我希望这个演练能帮助那些有兴趣用Easybase的useReturn hook部署生产就绪的React/React Native应用的人熟悉。