最佳 React 数据网格:FlexGrid





0/5 (0投票)
在本文中,我将介绍如何仅使用 Facebook 的 Create React App、Wijmo 和几行 JavaScript,将 React 中最好的数据网格——FlexGrid——添加到您的 Web 应用程序中。
虽然 React 近期才在前端 Web 开发领域普及起来,但自用户界面 (UI) 出现以来,数据网格一直是其中最常见的视觉软件元素之一。React 的流行和数据网格的普及意味着有大量的选项可供您添加到应用程序中。但在开源、商业和自研网格中,有一个解决方案脱颖而出。
GrapeCity 的 Wijmo 中包含的 FlexGrid 是 React 中最佳的即插即用数据网格。
在本文中,我将讨论
- 数据网格作为 UI 元素如此有用的原因
- FlexGrid 如何超越所有其他数据网格组件
- 一些示例,展示了在 React Web 应用程序中使用 FlexGrid 的简便性
真的,在 React 中使用 FlexGrid 非常简单,甚至很有趣!如果您不相信我,请 免费下载 Wijmo 并跟随操作!
为什么需要数据网格?
即使您不是开发者,您可能也熟悉数据网格的模式。分组和排序数据不仅仅是细致入微者的减压阀,更是人之本能。数据网格迎合了我们所有人快速获取和理解尽可能多数据的本能。
数据网格的实用性和普遍性使其得以流传至今,但并非没有演变。无疑,数字时代屏幕的出现极大地改变了数据网格。
现在,网格不仅在显示数据方面,在编辑数据方面也比以往任何时候都更有用。您能想象在 Microsoft Word 中创建销售报告或月度预算吗?借助自动排序和自动完成等便利功能,以及自定义公式等高级功能,Microsoft Excel 将是大多数用户的自然选择。
除了这些有益且节省时间的功能之外,大多数用户还会自然选择 Excel,因为他们在 PC 上第一次体验数据网格时就使用了它。Excel 是熟悉的。任何优秀的 UX 设计师或业务分析师都会告诉您:教育用户既困难又昂贵。这是数据网格普遍存在的最大优势之一——它们是直观的。
尽管 Excel 已成为使用数据网格的软件中最常见的例子,但这种模式已遍布软件领域。随着 Web 应用程序继续取代我们习惯的传统原生应用程序,数据网格也自然而然地进入 Web 应用程序 UI 是合乎逻辑的。在 GrapeCity,我们发现各行各业都在新 Web 应用程序项目中包含数据网格 UI 组件。
无论您是构建一个用于跟踪制造统计数据还是分析金融数据的应用程序,我敢打赌您已经考虑过数据网格所带来的直观性。而且,正如我们将看到的,Wijmo 的 FlexGrid 通过提供最佳的数据网格 UX 和最佳性能,可以最大限度地提高您的投资回报。
Wijmo FlexGrid:最佳数据网格组件解决方案
因此,现在我们知道使用数据网格的最大优势是它们本质上是直观的。这对最终用户体验来说是一个巨大的优势,但对于编写应用程序的开发人员来说又如何呢?从头开始创建数据网格 UI 组件对任何开发团队来说都绝非易事。完全有可能为您的应用程序构建一个数据网格的成本与教育用户使用您创建的另一个 UI 组件的成本一样高。
幸运的是,FlexGrid 就此应运而生!只需使用 Wijmo 脚本将 FlexGrid 添加到您的应用程序中,并将其连接到数据源即可。搞定!只需几个简单的步骤,您的应用程序就包含了一个功能齐全的数据网格!如果使用数据网格的最大优势是它能创造更好的最终用户体验,那么使用第三方数据网格的好处就是它能创造更好的开发体验。
稍后,我将通过一些示例展示 Wijmo 的 FlexGrid 如何通过开箱即用的 React 互操作性最大化开发工作流程的效率。但首先,让我们探讨一下 FlexGrid 作为可插入数据网格组件的一般特性。
FlexGrid 包:高性能、可扩展、熟悉且占用空间小
创建 React 中最强大的数据网格组件并非一蹴而就。事实上,FlexGrid 最初于 1996 年使用 C++ 为 Visual Basic 编写(甚至作为 Visual Studio 的一部分发布)。此后,它在许多平台中不断演进和完善,最近一种是 JavaScript。
几十年的打磨造就了一个高性能的网格,并提供了广泛的文档化 API。但 FlexGrid 能够长期保留其名称是有原因的。“Flex 理念”的强调对我们来说是重中之重。
控件应仅包含所需的核心功能集,并通过扩展模型提供其他所有功能。
排序、分组和编辑等功能是内置的,而其他附加功能则作为 FlexGrid 的可选扩展提供。这使得控件小巧而快速,使我们和客户都能够构建自定义功能。
性能
许多 React 开发者选择该框架是因为它注重开箱即用的性能和轻量级特性。FlexGrid 确保您无需牺牲这些优势中的任何一个,因为它同样注重保持速度和占用空间小的特点。
Wijmo 开发团队不断地对 FlexGrid 与其他第三方数据网格控件进行基准测试,以确保我们提供最快的网格。我们还通过不要求任何外部依赖来保持 FlexGrid 的占用空间小。事实上,FlexGrid 只会为您的应用程序增加约 25KB(gzip 压缩后)的大小。(您可以使用 Wijmo 的免费在线演示自行运行基准测试。)
最后,与 React 处理组件 DOM 的方式类似,FlexGrid 对其所有子元素进行虚拟化。您可以在几秒钟内加载数百万行数据——所有这些都在客户端上完成。这对于 React 互操作性来说极其重要。FlexGrid 是性能最佳的 React 数据网格,因为它与 React 高度优化的虚拟化算法无缝协作。
熟悉度
FlexGrid 几乎所有的交互行为都基于 Excel,正如前面所说,Excel 可能是任何最终用户最常用的网格/表格。人们在滚动、单击,尤其是在使用键盘命令(包括剪贴板功能)时,会期望某些行为。
我们不发明自己的行为,而是模仿 Excel 的行为,最终用户会立即感到舒适地使用我们的网格。令人惊讶的是,许多其他网格要么发明自己的行为,要么不完全支持键盘操作或滚动。例如,如果您在网格中选择一行并按住向下箭头键,许多网格的功能可能不如您预期的那样。
可访问性
我们非常高兴我们的 FlexGrid 控件现在为最流行的现代浏览器提供了全面的可访问性支持。使数据网格这样密集且复杂的控件变得可访问充其量是一种难题,而且没有其他网格组件能提供 FlexGrid 级别的框架互操作性和可访问性。
如果您想详细了解 FlexGrid 如何让所有人的数据都可访问,请阅读我们关于可访问性开发的博客文章。无论您是否有独立的无障碍团队,都可以通过使用 FlexGrid 来确保您的应用程序符合最新标准。
FlexGrid 和 React:天作之合的 Web 开发
FlexGrid 的功能远不止无缝集成 React 强大、高性能的渲染引擎。我们的网格组件使在 Web 应用程序 UI 中添加数据网格变得有趣、简单且自然。
现在来点有趣的!让我们看一些具体的例子,来说明 FlexGrid 的 React 互操作性所带来的优势。
使用 create-react-app
快速上手
Wijmo 提供了所有组件(包括 FlexGrid)的 CommonJS Node 模块!由于 Wijmo 是为 React 编写的互操作层,您可以像导入任何其他 React 类组件一样导入 FlexGrid。
所有这些都意味着您可以创建一个 React 项目,并只需几个简单的步骤即可添加 FlexGrid。得益于 Facebook 非常有用的 create-react-app
项目,您甚至无需担心引导或工具设置。
为了说明这一点,让我们通过一个简单的示例来介绍如何在 Create React App 项目中设置 FlexGrid。这是最终产品外观的预览
为方便起见,假设您已经按照步骤安装了 create-react-app
,并且您已将 Wijmo 下载到 C:\Users\dev\wijmo
。
在 C:\Users\dev
目录中,创建项目并添加 Wijmo 所需的全部操作如下:
create-react-app grid-ui cd grid-ui npm install --save ../wijmo/C1Wijmo/NpmImages/wijmo-commonjs-min
恭喜!现在您有了一个完全引导的 React 应用,可以使用 Wijmo。如果您想访问 FlexGrid 组件,只需在 src/App.js
顶部附近添加这一行:
import { FlexGrid } from 'wijmo/wijmo.react.grid';
就是这样!现在您已完全可以访问 FlexGrid 及其 API,甚至可以直接在 JSX 标记中使用它!接下来让我们看看如何做到这一点。
在 JSX 标记中声明网格
继续上面的 create-react-app
示例,让我们看看既然已经将 Wijmo 代码导入到 App 组件中,我们如何实际渲染 FlexGrid。
App 类中的 render()
函数中会有一些默认的 JSX 标记。请删除该默认 JSX,并用以下内容替换:
<div className="App">
<FlexGrid
itemsSource={this.state.data}
className="grid"
/>
</div>
对于一个 5 行代码的片段来说,这里面发生了很多事。在我谈论在标记中声明组件的重要性之前,让我们完成这个示例。您能发现为了正确加载 FlexGrid 我们还缺少什么吗?
您可能已经注意到,itemsSource
FlexGrid 属性引用了 App 组件的状态。由于 App 目前没有定义默认状态,FlexGrid 将是空白的!要解决此问题,只需在 App 类组件中添加以下代码即可:
state = {
data: [
{
id: 0,
name: 'Christian',
job: 'React Dev',
},
{
id: 1,
name: 'Connor',
job: 'Front-End Web Dev',
},
{
id: 2,
name: 'Sarah',
job: 'Chemist',
},
],
};
现在,通过运行 yarn start
(或 npm start
)来启动您的应用程序,并惊叹于 FlexGrid 的所有荣耀!在继续探索 FlexGrid 在 React 中提供的更有趣的功能之前,请思考一下能够完全在标记中声明一个复杂的数据网格控件的意义。
如果您迫不及待想看到这个应用程序的实际效果,请查看 Glitch 上的实时示例。
声明式标记非常适合遵循 MVVM 设计模式,我们可以在视图(标记)中完全配置我们的组件。FlexGrid 支持使用自然、未扩展的 JSX 标记来声明其整个 API。您可以完全在标记中设置属性、附加事件,甚至配置子组件(如列)。
您可能已经猜到,FlexGrid 的 itemsSource
连接到组件状态有一些特别之处。稍后会详细介绍……
轻松将 FlexGrid API 事件绑定到组件函数
既然将 FlexGrid 属性附加到组件状态如此容易,那么将 FlexGrid 事件附加到处理函数也应该是一件轻而易举的事!如果您在阅读上一节后有此想法,那么您完全正确!就像 App 组件状态中的数据可以通过 props 传递给 FlexGrid 一样,组件函数也可以作为 FlexGrid 事件的处理程序传递。
为了演示,让我们为示例添加一个新功能。由于我们已经有一个正在运行的 FlexGrid 和一些数据,我们可以访问 FlexGrid API 支持的数十个事件。我们将利用其中两个事件——beginningEdit
和 cellEditEnded
——在用户开始编辑网格中的单元格时显示描述性消息。
但在此之前,请仔细检查以确保您的 App
组件(在 App.js
中)的代码如下所示:
import React, { Component } from 'react';
import { FlexGrid } from 'wijmo/wijmo.react.grid';
import logo from './logo.svg';
import './wijmo.min.css';
import './App.css';
class App extends Component {
state = {
users: [
{
id: 0,
name: 'Christian',
job: 'React Dev',
},
{
id: 1,
name: 'Connor',
job: 'Front-End Web Dev',
},
{
id: 2,
name: 'Sarah',
job: 'Chemist',
},
],
};
render() {
return (
<div className="App">
<FlexGrid
itemsSource={this.state.users}
className="grid"
/>
</div>
);
}
}
export default App;
既然我们有了共同的基础,让我们继续添加编辑跟踪功能!首先,我们需要在 App 组件的状态中添加一些新属性来跟踪单元格是否处于编辑模式,以及处于编辑模式的单元格的坐标。请将这两个属性添加到 App 类的 state 属性中:
cellEdit: false, // whether or not any cell is in edit mode
editingCell: '', // a string representation of the coordinates for the cell being edited
默认情况下,当应用程序加载时,我们假设单元格不可能处于编辑模式。接下来,我们应该添加一个文本元素,当单元格处于编辑模式时可见,当没有单元格处于编辑模式时隐藏。为了给用户提供额外的上下文,文本将包含他们正在编辑的单元格的坐标。
为了确保我们有帮助、信息丰富的文本能够被注意到,我们将将其放在一个 <h2>
标签中。在 FlexGrid JSX 标记正下方添加以下内容:
<h2 style={{ display: this.state.cellEdit ? 'block' : 'none' }}>
Currently editing cell at {this.state.editingCell}
</h2>
所有这些代码都是非常基础的 React——简直是小菜一碟,对吧?!现在是重头戏……将我们创建的新状态属性连接到 FlexGrid 事件。这是否需要一些复杂的观察者处理程序或 React 反模式,对吧?
不!我们可以像您期望自己编写的 React 组件一样,将 FlexGrid 的事件挂钩到组件类函数。首先,将处理程序函数添加到 App 类中:
beginEdit(args) {
// Cell is now being edited - cell coords are passed in args object
this.setState({
cellEdit: true,
editingCell: `${args.col}, ${args.row}`,
});
}
finishEdit() {
// Cell edit was committed or canceled - make sure the editing alert is hidden
this.setState({
cellEdit: false,
});
}
同样,这些都是非常直接的 React 组件函数,用于根据单元格是否处于编辑模式来更新状态。请注意传递给 beginEdit
函数的 args
对象。FlexGrid 会自动将其相关的上下文传递给所有事件处理程序。要找出特定事件传递了什么信息,请查阅广泛的FlexGrid API 文档。
现在,为了完成这个便捷的新功能,我们必须告诉 FlexGrid 实际调用这些事件处理程序。幸运的是,因为 FlexGrid 是一个原生的 React 组件,我们可以轻松地在标记中挂钩这些处理程序。只需将这些属性添加到 FlexGrid 声明中:
beginningEdit={(sender, args) => {
this.beginEdit(args);
}}
cellEditEnded={() => {
this.finishEdit();
}}
完成所有工作后,您的 App
类应该如下所示:
class App extends Component {
state = {
users: [
{
id: 0,
name: 'Christian',
job: 'React Dev',
},
{
id: 1,
name: 'Connor',
job: 'Front-End Web Dev',
},
{
id: 2,
name: 'Sarah',
job: 'Chemist',
},
],
cellEdit: false,
editingCell: '',
};
beginEdit(args) {
this.setState({
cellEdit: true,
editingCell: `${args.col}, ${args.row}`,
});
}
finishEdit() {
this.setState({
cellEdit: false,
});
}
render() {
return (
<div className="App">
<FlexGrid
itemsSource={this.state.users}
className="grid"
beginningEdit={(sender, args) => {
this.beginEdit(args);
}}
cellEditEnded={() => {
this.finishEdit();
}}
/>
<br />
<h2 style={{ display: this.state.cellEdit ? 'block' : 'none' }}>
Currently editing cell at {this.state.editingCell}
</h2>
</div>
);
}
}
设置好后,运行您的应用程序并在浏览器中进行尝试!当您准备好惊叹时,只需双击 FlexGrid 中的任何单元格。瞧!beginningEdit
事件立即触发,App 组件的状态更新,显示带有正确单元格坐标的标签。如果您单击网格外部,标签应该会消失。
通过数据绑定自动更新 FlexGrid
现在我们的应用程序可以处理在网格中显示一些二维数据并响应用户编辑事件,也许提供一个表单让用户添加到数据列表中会很有帮助。提供一个更新状态的表单应该不难,但是我们如何确保更改会传播到 FlexGrid 呢?还记得前面注意到绑定的 itemsSource
FlexGrid 属性吗?如果您猜到将状态属性传递给 FlexGrid 会自动提供双向数据绑定,那么您猜对了!
由于网格的数据已经绑定到状态属性,应用程序需要做的就是更新该属性,FlexGrid 就会相应地更新。由于我们系统中的每个用户都有两个独特的特征——姓名和工作——请将两个对应的属性添加到 App
类的 state 中:
nameInput: '',
jobInput: '',
接下来,添加几个快速函数,这些函数将使用“添加用户”表单的输入来更新状态,并允许 App 组件将新对象添加到状态化的用户数组中:
addUser() {
if (this.state.nameInput.length === 0 || this.state.jobInput.length === 0) {
return;
}
this.setState({
users: [
// Create a new array, but keep the previous users using ES2015 spread operator
...this.state.users,
// Add the new user to the array
{
id: this.state.users.length,
// Use the input values saved in state
name: this.state.nameInput,
job: this.state.jobInput,
},
],
// Reset the inputs to blank in the process
nameInput: '',
jobInput: '',
});
}
updateInputValue(name, evt) {
// Inputs in markup will pass a state property name to set
// Retrieve the input value manually from the DOM event object
this.setState({
[name]: evt.target.value,
});
}
同样,这些函数只是普通的 React 组件类函数,它们允许组件以特定方式更新应用程序的状态。最后一步是添加表单的实际标记:
<h2>Add User</h2>
<div>
<label htmlFor="userName">Name: </label>
<input
type="text"
id="userName"
value={this.state.nameInput}
onChange={evt => this.updateInputValue('nameInput', evt)}
/>
</div>
<br />
<div>
<label htmlFor="userJob">Job: </label>
<input
type="text"
id="userJob"
value={this.state.jobInput}
onChange={evt => this.updateInputValue('jobInput', evt)}
/>
</div>
<br />
<input type="button" value="Add User" onClick={() => this.addUser()} />
请注意,我们在这里生成了两个 <input>
元素,它们在修改时都调用同一个 updateInputValue
函数。这两个函数调用的重要区别在于作为第一个参数传递的属性名称。如果您将这些参数与您刚刚添加到 state
对象中的属性进行比较,您会发现它们完全匹配。updateInputValue
函数使用此参数来确保新用户的职位和姓名属性不会混淆!
我知道悬念令人难熬,但在您尝试一下之前,请仔细检查您的代码与最终的 App
类代码:
class App extends Component {
state = {
users: [
{
id: 0,
name: 'Christian',
job: 'React Dev',
},
{
id: 1,
name: 'Connor',
job: 'Front-End Web Dev',
},
{
id: 2,
name: 'Sarah',
job: 'Chemist',
},
],
nameInput: '',
jobInput: '',
cellEdit: false,
editingCell: '',
};
addUser() {
if (this.state.nameInput.length === 0 || this.state.jobInput.length === 0) {
return;
}
this.setState({
users: [
// Create a new array, but keep the previous users using ES2015 spread operator
...this.state.users,
// Add the new user to the array
{
id: this.state.users.length,
// Use the input values saved in state
name: this.state.nameInput,
job: this.state.jobInput,
},
],
// Reset the inputs to blank in the process
nameInput: '',
jobInput: '',
});
}
updateInputValue(name, evt) {
// Inputs in markup will pass a state property name to set
// Retrieve the input value manually from the DOM event object
this.setState({
[name]: evt.target.value,
});
}
beginEdit(args) {
this.setState({
cellEdit: true,
editingCell: `${args.col}, ${args.row}`,
});
}
finishEdit() {
this.setState({
cellEdit: false,
});
}
render() {
return (
<div className="App">
<FlexGrid
itemsSource={this.state.users}
className="grid"
allowAddNew={true}
beginningEdit={(sender, args) => {
this.beginEdit(args);
}}
cellEditEnded={() => {
this.finishEdit();
}}
/>
<br />
<h2>Add User</h2>
<div>
<label htmlFor="userName">Name: </label>
<input
type="text"
id="userName"
value={this.state.nameInput}
onChange={evt => this.updateInputValue('nameInput', evt)}
/>
</div>
<br />
<div>
<label htmlFor="userJob">Job: </label>
<input
type="text"
id="userJob"
value={this.state.jobInput}
onChange={evt => this.updateInputValue('jobInput', evt)}
/>
</div>
<br />
<input type="button" value="Add User" onClick={() => this.addUser()} />
<br />
<h2 style={{ display: this.state.cellEdit ? 'block' : 'none' }}>
Currently editing cell at {this.state.editingCell}
</h2>
</div>
);
}
}
如果一切看起来都不错,那么开始吧!您应该看到一个简短、诱人的表单,其中包含两个诱人的字段,它们在呼唤“创建新用户!”。如果您屈服并填写您的新创作,您应该会立即看到网格更新了您刚添加的用户。这就是 FlexGrid 支持原生双向数据绑定的魔力。
我们创建的表单演示了应用程序状态和 FlexGrid 之间的双向数据绑定设置的一个方向。但另一个方向呢?好吧,如果您使用始终有用的 React Developer Tools,您可以立即看到它发挥作用!只需打开开发工具并检查应用程序的状态。您应该会看到 users
数组包含所有当前用户对象。如果您编辑网格中的一行(通过双击单元格),您会看到应用程序的状态立即更新以反映更改。
就是这样!使用 FlexGrid 和 React 实现快速、简单且自然的双向数据绑定!我不会用更多的代码来打扰您,但我会给您最后一个提示:使用 FlexGrid 可以更轻松地完成我们刚才所做的操作。(如果您对此感到好奇,请查看 FlexGrid 的 allowAddNew 属性。)
迈出下一步:构建您自己的 FlexGrid 应用!
如果您和我一样是 React 的忠实拥趸,并且上面的 FlexGrid 示例引起了您的兴趣,那么您不妨亲自尝试 Wijmo!加入成百上千使用 Wijmo 来加速其 UI 开发工作流程的个人用户和公司。
“我们购买了 Wijmo,他们的团队做得非常出色:外观精美、架构考虑周全;文档齐全;而且他们能像其他人一样跟上不断变化的环境。” — BJ Jeong,思科
如果我的话仍未说服您,我鼓励您尝试 FlexGrid 并证明我是对的还是错的。如果我们错了,而您找到了更好的数据网格解决方案,我们希望知道。我们 20 年来一直在不断完善和改进我们的网格,而且我们不会就此停步。
在 Glitch 上运行示例
许可证
GrapeCity 或 Wijmo 分发的所有内容和文件均根据 GrapeCity EULA 进行许可。