Label text not updating in MUIDataTable ReactJS - javascript

I want to add multi language option in mui Datatables. I can change the translations but when I want to change language, I tried to give another object with the other translations (this object if I do console log I can see the changes) but the label texts not change.
I used a contextProvider to change the language selected and then get the specific dictionary with the translations.
Is a class component, so I did a static contextType with the correct provider.
Is there any possibility to re-render the element with another options or something like that?
options = {
textLabels: this.context.translation.dataTables.textLabels
};
return(
<MUIDataTable
title={this.context.language.value}
data={data}
columns={columns}
options={options}
/>
);

The best approach to re-render Mui-Datatables its updating the key of the table
key={this.context.language.value}
<MUIDataTable
key={this.context.language.value}
title={this.context.language.value}
data={data}
columns={columns}
options={options}
/>

You can force React component rendering:
There are multiple ways to force a React component rendering but they are essentially the same. The first is using this.forceUpdate(), which skips shouldComponentUpdate:
someMethod() {
// Force rendering without state change...
this.forceUpdate();
}
Assuming your component has a state, you could also call the following:
someMethod() {
// Force rendering with a simulated state change
this.setState({ state: this.state });
}

use customRowRender Function in the options and manipulate table with respect to language
Override default row rendering with custom function.
customRowRender(data, dataIndex, rowIndex) => React Component

In MUIDataTable, We can override label name by providing label in MUIDataTableColumnDef options while making column.
Example :
const columns: MUIDataTableColumnDef[] = [
{
name: 'Id',
label: 'ID',
options: {
download: false,
customBodyRenderLite: (index: number) => {
const desc: Description = evenMoreAbout[index]
return <BasicInfo obj={desc} setIconClicked={setIconClicked} />
}
}
},
{
name: 'id',
label: 'ID',
options: {
display: 'excluded',
download: true,
customBodyRender: desc => desc.id
}
}]
Even though if we still want to over ride the label name on some condition of data using customHeadLabelRender ... we can as like below example
const columns: MUIDataTableColumnDef[] = [
{
name: 'Id',
label: '',
options: {
download: false,
customBodyRenderLite: (index: number) => {
const desc: Description = evenMoreAbout[index]
return <BasicInfo obj={desc} setIconClicked={setIconClicked} />
},
customHeadLabelRender: (dataIndex: number, rowIndex: number) => {
return 'ID';
}
}
}
]

Related

React Native and Typescript - Need an approach to create a flexible table component

I'm trying to create a table component that can display its cells flexibly. Every cells on a column displays the same cell component. The table component take an array of object as initial data and an array of column properties. Table should looks like this:
interface MyTableProps {
columns: ColumnProps[];
initialData: Array<any>; // Should array of object
callbackTableDataChanged: (newData) => void; // being used to send modified table data back to MyTable's father components.
}
interface ColumnProps {
label: string;
objectPropertyName: string; // (*)
renderCell: "cell-type-1" | "cell-type-2" | "cell-type-3" | ... // (**)
}
const MyTable: React.FC<MyTableProps> = tableProps => {
// The table should have its own a copy of initial data, because of many reasons
const [tableData, setTableData] = useState(tableProps.initialData);
const handleDataChange = (thePropertyName: string, rowIndex: number, newValue: any) => {
// This function changes the table data array,
// whenever the cell component (MyCellComp1, MyCellComp2, ...) on a column make a change of value.
// For example: MyCellComp1 is a time picker, whenever a new timestamp selected,
// it will send new time value through a callback, to its father component (aka MyTable).
// Then, this func will do the changes to the corresponding "place" in the data array object of this MyTable.
}
const renderRows = (column: ColumnProps, rowIndex: number) => {
switch (column.renderCell) {
case cell-type-1: return <CellComp1 ... onDataChanged={cellData => handleDataChange(column.objectPropertyName, rowIndex, cellData)}/>
case cell-type-2: return <CellComp2 ... onDataChanged={cellData => handleDataChange(column.objectPropertyName, rowIndex, cellData)}/>
...
}
}
return (
{renderColumnLabels}
<Flatlist data={tableData} renderItem={({col, index}) => renderRows(col, index)}/>
)
}
(*): "objectPropertyName" is a string that has value which is a name of one of the properties of initial object data. Forgive my awkward grammar!
(**): I use "renderCell" to tell the table what it should render at specific column. All cells on a column have the same component type.
The table should be used like this:
const initialData = [
{id: "123", name: "Tom"}, {id: "456", name: "Jerry"}, ...
]
const columns: ColumnProps[] = [
{label: "The ID", objectPropertyName: "id", renderCell: "cell-type-1"},
{label: "The name", objectPropertyName: "name", renderCell: "cell-type-2"},
...
];
<MyTable columns={columns} initialData={initialData} ... />
// This is how table looks like:
// column 1 column 2
// labels row: The ID The name
// row 1 : <CellComp1 /> <CellComp2 />
// row 2 : <CellComp1 /> <CellComp2 />
// ...
The problem is that with above approach, with the way I declared how cells on a column should be rendered, it limits the variety of cell component that MyTable could display. I declare 3 values for ColumnProps.rendercell, columns can display only 3 kinds of cell components.
My solution is that ColumnProps will have a new props which is a function that return a component:
interface ColumnProps {
label: string;
objectPropertyName: string; // (*)
renderCell: "cell-type-1" | "cell-type-2" | "cell-type-3" | ... // (**)
renderCellComponent?: () => JSX.Element
}
The new "renderCellComponent" prop will replace default cell components with whatever it returns.
However, with is method, I cant change the data array of MyTable with its function "handleDataChange".
For example:
const initialData = [
{id: "123", name: "Tom"}, {id: "456", name: "Jerry"}, ...
]
const columns: ColumnProps[] = [
{label: "The ID", objectPropertyName: "id", renderCell: "cell-type-1", renderCellComponent: () => <NewCellComp1 ... />},
{label: "The name", objectPropertyName: "name", renderCell: "cell-type-2", renderCellComponent: () => <NewCellComp2 ... />},
...
];
<MyTable columns={columns} initialData={initialData} ... />
As you can see, NewCellComp1 and NewCellComp2 need to access the function "handleDataChange" of MyTable just like the way CellComp1 and CellComp2 did. In order to do that, I tried to use React.forwardRef and useImperativeHandle to MyTable:
const MyTable: React.FC<MyTableProps> = React.forwardRef((tableProps, ref) => {
...
useImperativeHandle(ref, () => {
refFuncHandleDataChange(objectPropertyName: string, rowIndex, newValue) {
handleDataChange(objectPropertyName, rowIndex, newValue);
}
})
return ...
})
Now, MyTable should be used like this:
const initialData = [
{id: "123", name: "Tom"}, {id: "456", name: "Jerry"}, ...
]
const columns: ColumnProps[] = [
{...other props, objectPropertyName: "id", renderCellComponent: () => <NewCellComp1 onDataChanged={data => refTable.current?.refFuncHandleDataChange("id", ..., data)} />},
{...other props, objectPropertyName: "name", renderCellComponent: () => <NewCellComp2 onDataChanged={data => refTable.current?.refFuncHandleDataChange("name", ..., data)} />},
...
];
const refTable = useRef();
<MyTable ref={refTable} columns={columns} initialData={initialData} ... />
As you can see, with this approach, I still lack the row index of the cell component that make a change of data. When cell rendering is declared inside MyTable, I can access to row index easily thanks to Flatlist, but outside MyTable does not offer that luxury, because "renderCellComponent" function of ColumnProps is a declaration for all cells on a column. This is where I'm stuck.
In conclusion, I want to create a table component that be able to display every kind of components on its cells. Also, keep its properties as "simple" as possible. You can see that I'm currently must declare only data and columns props. But my approach seems to be impossible to do that.
Can you share me an improvement for what I did or an entirely new approach for this problem. Thank you!

AG Grid React: How to get the state of rows after changing the order?

After implementing the drag and drop feature on AG Grid table, I'm looking for a way to get the current state with the updated order/index of rows. My goal is to persist the table data after changing the order, but can't find the respective state of the current order.
I'd appreciate any help or any idea.
Sandbox demo and example code below
import React from "react";
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
function App() {
const [gridApi, setGridApi] = React.useState(null);
const [gridColumnApi, setGridColumnApi] = React.useState(null);
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
};
const defaultColDef = {
flex: 1,
editable: true
};
const columnDefs = [
{
headerName: "Name",
field: "name",
rowDrag: true
},
{ headerName: "stop", field: "stop" },
{
headerName: "duration",
field: "duration"
}
];
const rowData = React.useMemo(
() => [
{
name: "John",
stop: 10,
duration: 5
},
{
name: "David",
stop: 15,
duration: 8
},
{
name: "Dan",
stop: 20,
duration: 6
}
],
[]
);
return (
<div>
<h1 align="center">React-App</h1>
<div>
<div className="ag-theme-alpine" style={{ height: "700px" }}>
<AgGridReact
columnDefs={columnDefs}
rowData={rowData}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
rowDragManaged={true}
></AgGridReact>
</div>
</div>
</div>
);
}
export default App;
You can get the order of the rows inside the grid by iterating over them using the Grid API method forEachNode:
API for Row Nodes
const rows = [];
gridApi.forEachNodeAfterFilterAndSort((node) => rows.push(node.data));
console.log(rows);
See this implemented in the following sample.
You're currently using managed dragging by passing rowManagedDragging={true}, which means the AgGridReact component is managing the row order state.
If you want to maintain row order state outside the component, you need to use Unmanaged Dragging.
Add a handler for onRowDragMove, and use the node and overIndex or overNode properties of the event to update your local event order state, and pass it to the AgGridReact component to re-render.
Take a look at this example from the docs

`react-table` v7 how to preserve a grouping's sort order whilst the rows of the groups are sorted

I have a table that is grouped with an initial sort order applied.
I want to be able to sort on the UI but at the same time preserve the initial grouping sort order.
So it would be like a multi-sort but that one sorting rule is always applied to the group.
This is the initial sorting rule:
const initialSortBy: Array<RisksIdentifiedSortBy> = [
{
id: DataAccessors.RISK_SCORE,
desc: true,
},
{
id: DataAccessors.GROUP,
desc: false,
},
];
RISK_SCORE column would be sortable:
{
Header: (): JSX.Element => {
return (
<Box sx={{ margin: 'auto' }}>
{t('policy-management/summary:TABLE.HDR.RISK_SCORE')}
</Box>
);
},
accessor: DataAccessors.RISK_SCORE,
sortType: 'alphanumeric',
Cell: ({
value,
row,
}: DataTableCell<RiskLevel>): JSX.Element | null => {
return !row.canExpand ? (
...
) : null;
},
},
And we would force RISK_GROUP to be sorted the same every time without being sortable itself from user interaction:
{
Header: t('policy-management/summary:TABLE.HDR.RISK_GROUP'),
accessor: DataAccessors.GROUP,
Cell: ({ value }: DataTableCell<string>): string => value,
SubCell: ({ row }: Pick<DataTableCell<any>, 'row'>): JSX.Element => {
const {
original: { riskCategory },
} = row;
return <Box sx={{ ml: '1.5rem' }}>{riskCategory}</Box>;
},
width: '20%',
disableSortBy: true,
},
Any ideas how to do this?
I think it would be similar to programmatically setting the sort option in one column when another is sorted?
useTable({ columns, data, autoResetSortBy: false }, useSortBy))
I managed to do this by passing a controlled state into my table as suggested here:
https://react-table.tanstack.com/docs/faq#how-can-i-manually-control-the-table-state
i was stuck with this for a while, turns out there is a property on table instance
const tableInstance = useTable<any>(
{ columns, data: memoData, autoResetExpanded: false, autoResetSortBy: false }, ...)
docs:
autoResetSortBy: Boolean
Defaults to true When true, the sortBy state
will automatically reset if any of the following conditions are met:
data is changed To disable, set to false For more information see the
FAQ "How do I stop my table state from automatically resetting when my
data changes?"
https://react-table.tanstack.com/docs/api/useSortBy

Can I access the props or state of a Pagination component being generated by an Ant Design Table?

I have an Ant Design Table in my code and I am passing it a pagination prop as follows:
<Table
dataSource={alerts}
pagination={{
pageSize: 9,
position: 'bottomLeft',
showSizeChanger: false
}}
columns={columns}
/>
In my React component tree, I can see a Pagination component, that has both a props and state value of current (tells you/sets the current page you are on) that I want access to, so I can save it & pass it around.
However, I don't know how to get access to this value because I don't actually have a Pagination component in my React code, the Pagination component is just being generated by the pagination prop in the Table component. Is it possible to access this value somehow?
You can't access the Pagination props and state from the component above the Table/Pagination. That's against how React works, because the data here flows only in one direction. From top components to bottom components.
However, there is a way, the pagination properties that you need, can be returned via Pagination event handlers. So, you could save them in state in the component that uses Antd Table.
At the code below I am using API provided by the Table component. The onChange and onShowSizeChange return the actual Pagination state values, that then I save to the state. I was referring to the documentation of Pagination component here.
const dataSource = [{
key: '1',
name: 'Mike',
age: 32,
address: '10 Downing Street',
},
{
key: '2',
name: 'John',
age: 42,
address: '10 Downing Street',
},
];
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
class App extends React.Component {
state = {
currentPageSize: 1,
currentPage: 1,
}
onPageChange = (page: number, pageSize ? : number) => {
this.setState({
currentPage: page,
currentPageSize: pageSize
});
}
onPageSizeChange = (page: number, pageSize: number) => {
this.setState({
currentPage: page,
currentPageSize: pageSize
});
}
render() {
return ( < Table dataSource = {
dataSource
}
pagination = {
{
pageSize: this.state.currentPageSize,
current: this.state.currentPage,
onChange: this.onPageChange,
onShowSizeChange: this.onPageSizeChange
}
}
columns = {
columns
}
/>);
}
}
render( < App / > , document.getElementById("root"));

Generic method to render React component depends on two props

I've got a component that gets two props, function and node(or string with label text), depends on these props I render the icon with some label. In future, I'm going to add more button and want to create the generic method that rendered this icon more flexible. So how to create such generic method for that?
const Wrapper = ({onRefresh, onExportToExcel, actionsLabel}) => {
return
{onRefresh && !!actionsLabel.refresh &&
<InlineIconButton name='refresh' label={actionsLabel.refresh} onClick={onRefresh} icon={<Autorenew/>} aria-label="Refresh"/>}
{onExportToExcel && !!actionsLabel.exportToExcel &&
<InlineIconButton name='exportToExcel' label={actionsLabel.exportToExcel} onClick={onExportToExcel} icon={<FileDownload/>} aria-label="ExportToExcel"/>}
}
<Wrapper onRefresh={()=> {}} onExportToExcel ={()=> {}} actionLabel={refresh: 'refresh', exportToExcel: 'export'}>
Maybe do something like:
const EXPORT_EXCEL = {
key: "EXPORT_EXCEL",
label: "export",
ariaLabel: "Export Excel",
icon: <Autorenew/>,
handler: params => { /* your function */ }
};
const REFRESH = {
key: "REFRESH",
label: "refresh",
ariaLabel: "Refresh",
icon: <FileDownload/>,
handler: params => { /* your function */ }
};
<Wrapper type={EXPORT_EXCEL} />;
const Wrapper = ({ type }) => {
return <InlineIconButton name={type.key} label={type.label} onClick={type.handler} icon={type.icon} aria-label={type.ariaLabel ? type.ariaLabel : type.label} />;
}
}
You even the possiblity to throw those EXPORT_EXCEL and REFRESH into array. Instead of having them loose put them in an array like so:
const BUTTONS = [
{
key: "EXPORT_EXCEL",
label: "export",
ariaLabel: "Export Excel",
icon: <Autorenew/>,
handler: params => { /* your function */ }
},
{
key: "REFRESH",
label: "refresh",
ariaLabel: "Refresh",
icon: <FileDownload/>,
handler: params => { /* your function */ }
},
];
And then loop through to create the Wrapper.
But then it's really up to you and your preferences and app's requirements
The entire idea behind React is to be able to create a unique component for every kind of usage. That is the entire philosophy behind React composability. Don't understand why would you want to wrap it.

Categories