问题描述
我有一个React材料表。我希望其中一列是我的自定义<DatePicker/>
组件,用户可以在其中为该行分配计划的日期。
表格currentley可以远程获取数据。
使用<DatePicker/>
组件并选择日期时,我不希望整个表从远程源重新获取数据。相反,我希望<DatePicker/>
onChange处理程序修改表中的当前数据(我将日期设置为该行的行)。
有没有办法进入材料表数据存储并调用“ setData”函数,以便仅更新目标行?
通常,如果表的数据不是来自远程数据源,我只会调用自己的“ setData”函数,但是由于我利用了构建在远程数据选项中的物料表并使用过滤器,因此我无法做到这一点。
解决方法
当数据来自远程源时,我无法找到一种干净的方法来利用物料表的数据并更新行。但是,我能够提出一种可行的“ hacky”解决方案。
为此,我执行了以下操作:
- 创建了一个表以引用物料表并将其存储在我的Redux商店中。
- 创建本地组件状态以存储我以前的查询参数历史记录。
- 将MaterialTable上的
data
道具设置为自定义getData
函数,该函数返回Promise,以命中后端api以获取表数据(所有行),或修改当前表数据,然后返回此新修改的数据。该函数使用tableRef.current.dataManager.getRenderState().data
获取当前的MaterialTable数据。然后返回它的修改版本(如果需要)。 - 创建了一个小型组件(NextAction),该组件需要2个道具-来自MaterialTable的
rowData
和来自Redux商店的tableRef
-呈现包含我的自定义操作按钮的列,将其单击以修改当前表数据。该组件还可以访问Redux存储中存储的物料表的tableRef。该组件(最终是一个按钮)利用tableRef上的onQueryChange()
函数来手动强制对getData函数的调用。 - 在我的列定义中设置渲染键,以返回名为NextAction的组件。
这个想法是要有一个带有按钮的列。按下按钮后,该行的“当前状态”列将被修改,并且不会从api重新呈现表数据。
之所以起作用,是因为单击该按钮时将调用tableRef.current.onQueryChange()
函数。因为组件的“数据”属性设置为自定义getData函数,所以在调用tableRef.current.onQueryChange()
时,告诉MaterialTable调用data
函数,该函数是我们在MaterialTable组件上设置的自定义getData函数。>
然后在此getData函数中,检查新查询参数是否与旧查询参数不同(因为查询参数也存储在本地状态)。如果它们相同,则检查getData查询参数是否具有nextState键。仅当从自定义按钮调用getData时,才存在nextState键。如果是这样,我们将使用tableRef来访问当前表数据,修改我们想要的行,然后返回新的tableData(仍包装在Promise中)。
以下是实现上述目标的示例代码:
import React,{ useContext } from 'react';
import { connect } from 'react-redux';
import axios from 'my/path/to/axios';
/*
Custom Button rendered in a column on the MaterialTable
Takes a tableRef prop from the Redux Store.
Note the onClick function that is called.
*/
const NextActionButton = connect(mapStateToPropsNextAction)(( props ) => {
const tableRef = props.tableRef; // captured from redux store - tableRef from MaterialTable
return (
<Button
disabled={false}
variant="contained"
color="secondary"
size="large"
onClick={(e) => {tableRef.current.onQueryChange({
onNextAction: true,nextState: 'dispatched',row_id: props.rowData.tableData.id,})}}
>
Dispatch
</Button>
)
})
const MyComponent = ( props ) => {
let tableRef = React.createRef() // Create the tableRef
props.setTableRef(tableRef)
// Store the query history
const [queryHist,setQueryHist] = React.useState({});
// Custom function to return the table data either via api or from the old state with modifications
const getData = (query,tableRef) => new Promise((resolve,reject) => {
const queryParams = {
filters: query.filters,orderBy: query.orderBy,orderDirection: query.orderDirection,page: query.page,pageSize: query.pageSize,search: query.search,}
/*
Here is the magic that allows us to update the current tableData state without hitting the api
If the query is the same as before,then just return the same table without hitting api
This occurs only when:
1.) the user types the same filter
2.) when the user wants to update a single row through the special update component in the update column
*/
if (JSON.stringify(queryHist) === JSON.stringify(queryParams)) {
// This is how we get MaterialTable's current table data. This is a list of objects,each object is a row.
let newData = tableRef.current.dataManager.getRenderState().data;
if (query.onNextAction === true) {
newData[query.row_id].current_state = query.nextState;
}
resolve({
data: newData,page: tableRef.current.state.query.page,totalCount: tableRef.current.state.query.totalCount,})
}
setQueryHist(queryParams) // Store query params in the local state for future comparison
axios.get('/my_data_api',{
params: queryParams
}).then(response => {
return resolve({
data: response.data.data,page: response.data.page,totalCount: response.data.total_count,})
}).catch(function (error) {
console.log(error);
})
})
const [columns] = React.useState([
{
title: "Record Id",field: "record_id",},{
title: "Current State",field: "current_state",// This is the field we will be updating with the NextActionButton
},{
title: "Next Action",field: "placeholder",filtering: false,render: (rowData) => <NextActionButton rowData={rowData}/> // Render the custom button component
},])
return (
<MaterialTable
title="My Table Title"
tableRef={tableRef} // Assign the ref to MaterialTable
data={
(query) => getData(query,tableRef)
}
columns={columns}
options={{
filtering: true,debounceInterval: 600,paging: true,pageSize: 50,pageSizeOptions: [50,100,1000],emptyRowsWhenPaging: false,selection: true,}}
.
.
.
/>
)
}
// Redux stuff
const mapDispatchToProps = dispatch => {
return {
setTableRef: (tableRef) => dispatch({type: 'SET_TABLE_REF','ref': tableRef})
.
.
.
};
};
export default connect(null,mapDispatchToProps)(Workorders);