Redux 是 JavaScript 状态容器,提供可预测化的状态治理。它能够用在 react、angular、vue 等我的项目中, 但与 react 配合应用更加不便一些。
Redux 原理图如下,能够看到 store 仓库是 Redux 的外围,通过保护一个 store 仓库治理 state。state 是只读的,惟一扭转 state 的办法就是组件触发 Action。通过编写 Reducers 函数,它会接管先前的 state 和 action,并返回新的 state。
Redux 的核心理念就是如何依据这些 action 对象来更新 state,强制应用 action 来形容所有变动带来的益处是你能够清晰地晓得利用中到底产生了什么。如果一些货色扭转了,你能够晓得为什么变动,action 就是形容产生了什么的指示器。
来看一下 Redux 在大屏展现中具体的应用场景:
上面的截图是一个产品开发中十分常见的大屏展现界面示例。外围的数据源为一组销售数据,上方三个仪表板以及下方的表格组件共享同一个数据源,实现了数据明细显示以及各维度的数据统计。
从图上来看,仿佛曾经具备了大屏展现的数据显示和统计性能,然而展现的数据是没有方法被编辑和批改的。此时,你可能会收到来自客户的灵魂拷问:
“展现性能曾经不错了,然而表格数据能够实时编辑更新吗?”
图中的销售明细数据是用 html 表格间接显示的,如果要实现编辑,通常的做法是,咱们筛选一个前端表格组件,实现编辑的性能。
文末可下载文章代码文件。
将表格增加到你的 React 应用程序
咱们要用电子表格替换这个 html 表格,批改 component 文件夹中的 SalesTable.js,替换其中的 table。
<SpreadSheets hostClass={config.hostClass} workbookInitialized={workbookInit} valueChanged={(e,info) => handleValueChanged(e,info)}>
<Worksheet name={config.sheetName} dataSource={tableData} autoGenerateColumns={config.autoGenerateColumns} >
<Column width={50} dataField='id' headerText="编号"></Column>
<Column width={200} dataField='client' headerText="客户"></Column>
<Column width={320} dataField='description' headerText="形容"></Column>
<Column width={100} dataField='value' headerText="销售额" formatter={config.priceFormatter} resizable="resizable"></Column>
<Column width={100} dataField='itemCount' headerText="数量"></Column>
<Column width={100} dataField='soldBy' headerText="销售人员"></Column>
<Column width={100} dataField='country' headerText="国家"></Column>
</Worksheet>
</SpreadSheets>
其中,SpreadSheets 元素创立了一个电子表格并定义了如何显示数据列。dataSource 属性定义了绑定的数据源,Column 中的 dataField 属性通知该列应该绑定底层数据集的哪个属性。
接下来是 js 代码局部,
import '@grapecity/spread-sheets-react';
import "@grapecity/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css";
import {SpreadSheets, Worksheet, Column} from '@grapecity/spread-sheets-react';
export const SalesTable = ({tableData, valueChangedCallback,} ) => {
const config = {
sheetName: 'Sales Data',
hostClass: 'spreadsheet',
autoGenerateColumns: false,
width: 200,
visible: true,
resizable: true,
priceFormatter: '$ #.00',
chartKey: 1
}
function handleValueChanged(e, obj) {valueChangedCallback(obj.sheet.getDataSource());
}
handleValueChanged.bind(this);
const [_spread, setSpread] = useState({});
function workbookInit(spread) {setSpread(spread)
}
}
只需很少的代码即可实现。config 中的几个数据属性。是绑定到电子表格中的组件的配置选项。workbookInit 办法是在初始化工作表时调用的回调。handleValueChanged 是在表格数据发生变化后的回调
从新运行,即可显示电子表格数据:
当初咱们用一个残缺的电子表格替换了原来的 html table,此时能够对表格中的数据做任意的批改编辑,然而在编辑后上方的销售统计后果并不会实时更新,接下来咱们就用 Redux 来创立一个 store 仓库用来存储销售数据,以实现数据的共享和实时更新。
将 Redux 增加到你的 React 应用程序
1. 引入相干库
"@reduxjs/toolkit": "^1.9.1",
"react-redux": "^7.2.0",
"redux": "^4.0.5"
2. 通过 createSlice 创立切片
新建一个 js 文件,写入上面的代码,通过 Redux 提供 createSlice 办法,咱们创立了一个切片,初始化了 state,在其中退出了销售明细数据作为 recentSales。为 reducers 增加了两个办法 updatesales 和 importSales,用于在销售明细数据更新或者导入这两种状况时,来同步 recentSales。
import {createSlice} from '@reduxjs/toolkit';
import {recentSalesdata} from "../data/data";
const initialState = {recentSales: JSON.parse(JSON.stringify(recentSalesdata)),
status: 'idle',
};
export const salesSlice = createSlice({
name: 'recentSales',
initialState,
reducers: {importSales: (state,action) => {state.recentSales=JSON.parse(JSON.stringify(action.payload));
},
updatesales: (state,action) => {
let sales=state.recentSales;
let arr=sales.map(function(o){return o.id});
console.log(arr);
action.payload.forEach((newsale)=>{if(arr.indexOf(newsale.id)>=0){state.recentSales[arr.indexOf(newsale.id)]=JSON.parse(JSON.stringify(newsale));
}
else{console.log("add");
state.recentSales.push(JSON.parse(JSON.stringify(newsale)));
}
});
},
},
});
export const {updatesales,importSales} = salesSlice.actions;
export const recentSales = (state) => state.recentSales.recentSales;
export default salesSlice.reducer;
3. 创立 store
增加 store.js 文件并退出上面的代码,这里创立的 store 中退出了刚刚创立的切片器。
import {configureStore} from '@reduxjs/toolkit';
import recentSalesReducer from '../store/salesSlice';
export const store = configureStore({
reducer: {recentSales: recentSalesReducer,},
});
4. 在 component 组件中应用 store
在 Dashboard.js 中,import 上面的代码。
import {useSelector, useDispatch} from 'react-redux';
import {
updatesales,importSales,
recentSales
} from '../store/salesSlice';
而后在创立的 Dashboard 办法体中,再退出上面的代码,其中 react-redux 提供的:
- useSelector 用于获取刚刚创立的 state 中的 recentSales。
- useDispatch 用于调用 reducer 中曾经创立的办法来更新 recentSales。
const sales = useSelector(recentSales);
const dispatch = useDispatch();
function handleValueChanged(tableData) {dispatch(updatesales(tableData));
}
function handleFileImported(newSales) {dispatch(importSales(newSales));
}
对大屏展现面板退出 redux 做了上述革新后,就达到了销售数据编辑后,数据统计后果同步更新的成果:
动图中能够看到下面三个仪表板显示的内容也同步进行了更新。起因是表格被编辑后,咱们同步更新了 state 中的 recentSales。
好了,当初咱们曾经有了一个能够随着数据变动而实时更新的增强型仪表板。客户的需要顺利完成,然而在演示时,你很可能又会听到客户说出的上面的需要:
“能反对 Excel 数据的导入导出吗?”
如果您曾经开发软件很长时间,您可能不止一次地从最终客户或者产品经理那里听到过这个灵魂拷问。对于非技术人群来说,感觉要求 Excel 导入 / 导出 / 展现是一个十分失常且容易实现的需要。
但实际上,这个问题经常让前端开发人员感到大刀阔斧。解决 Excel 文件须要大量工作。即便应用第三方的 grid 组件,也很难反对导入一个简单的 Excel 表格作为数据。
这个问题通过表格能够变得简略,导入和导入都能够间接实现。这也是咱们在开始时应用将电子表格作为表格明细数据显示和编辑控件的起因。上面咱们为利用退出 Excel 导入导出性能
导出为 Excel 文件
将 Excel 导入导出性能增加到工作表很容易。首先,在界面上增加相干的文件输入框和按钮。把它放在电子表格面板的底部,在 SpreadSheets 完结标记之后增加。
<div className="dashboardRow">
{/* EXPORT TO EXCEL */}
<button className="btn btn-primary dashboardButton"
onClick={exportSheet}>Export to Excel</button>
{/* IMPORT FROM EXCEL */}
<div>
<b>Import Excel File:</b>
<div>
<input type="file" className="fileSelect"
onChange={(e) => fileChange(e)} />
</div>
</div>
</div>
接下来增加点击时触发的 exportSheet 办法,首先增加并导入上面的包,其中 @grapecity/spread-excelio 是 SpreadJS 中用于导入导出 Excel 的包。
import {IO} from "@grapecity/spread-excelio";
import {saveAs} from 'file-saver';
而后将导出办法 exportSheet 增加到组件中:
function exportSheet() {
const spread = _spread;
const fileName = "SalesData.xlsx";
const sheet = spread.getSheet(0);
const excelIO = new IO();
const json = JSON.stringify(spread.toJSON({
includeBindingSource: true,
columnHeadersAsFrozenRows: true,
}));
excelIO.save(json, (blob) => {saveAs(blob, fileName);
}, function (e) {alert(e);
});
}
运行测试点击按钮,即可间接获取到导出的 Excel 文件。
须要留神的是,咱们设置了两个序列化选项:includeBindingSource 和 columnHeadersAsFrozenRows。以确保绑定到工作表的数据被正确导出,且工作表蕴含列题目,
Excel 数据导入
咱们持续来增加导入的办法,刚刚创立文件输入框,咱们来解决它的 onChange 事件,创立一个 fileChange 办法
function fileChange(e) {if (_spread) {
const fileDom = e.target || e.srcElement;
const excelIO = new IO();
const spread = _spread;
const deserializationOptions = {frozenRowsAsColumnHeaders: true};
excelIO.open(fileDom.files[0], (data) => {const newSalesData = extractSheetData(data);
spread.getSheet(0).setDataSource(newSalesData,false);
fileImportedCallback(newSalesData);
});
}
}
抉择文件后,应用 ExcelIO 导入它。获取其中的 json 数据。传入自定义的函数 extractSheetData,从中提取须要的数据,而后设置给 SpreadJS 作为电子表格数据源,另外传给 fileImportedCallback 办法,这个函数中会调用 dispatch(importSales(newSales)); 来同步更新了 state 中的 recentSales。
extractSheetData 函数能够在 src/util.util.js 文件中找到,用于 解析 Excel 中的数据。extractSheetData 函数假设导入工作表中的数据与原始数据集具备雷同的列。如果有人上传的电子表格不合乎此要求,将无奈解析。这个应该是大多数客户能够承受的限度。数据不符时,也能够尝试给客户一个提示信息。
Excel 导入导出成果
最终的我的项目能够参考上面的附件
https://gcdn.grapecity.com.cn…
React、Redux 和 电子表格的配合应用让这个利用的加强开发变的十分不便。借助 Redux 提供的可预测化的状态治理和交互式电子表格,能够在很短内创立简单的企业 JavaScript 应用程序。
拓展浏览
React + Springboot + Quartz,从 0 实现 Excel 报表自动化
电子表格也能做购物车?简略三步就能实现
应用纯前端类 Excel 表格控件 SpreadJS 构建企业现金流量表