Mui DataGrid > renderCell > useValue from renderCell - reactjs

I have a field in the Datagrid with renderCell. The Value i have to display will be fetched inside the AlertIssues-Component as the original Data just provides the uuid to fetch the corresponding data (issue amount). So props.row._id is use in AlertIssues with a hook to retrieve the issues of the Alert in this row.
{
field: "issues",
type: "number",
headerClassName: "iconHeader",
// valueFormatter: params => {
// console.log("value formater",params )
// useGetIssueValue cannot be used here: (hook rules)
// let theValue = useGetIssueValue(params.id)
// return theValue
// },
// useGetIssueValue cannot be used here: (hook rules)
// valueGetter: params => useGetIssueValue(params.id)
renderCell: (props: GridRenderCellParams<Number>) => {
// useGetIssueValue is used inside AlertIssues
// and works to display the right amount
return(
<AlertIssues {...props} />
)},
},
export const AlertIssues = (props: GridRenderCellParams<Number>) => {
const { row } = props;
// i am getting my amount here without trouble.
// but it is just the displayed value ...
const alertIssueAmount = useGetIssueValue(row.id);
...
return <>alertIssueAmount</>
i tried to use "valueGetter" or "valueFormatter" to get the amount i need. but inside these functions i am not allowed to call my useData-hook, as they are functions and not React-Components or Hooks.
the row itself does not have the value i got inside of AlertIssues...
i am very lost here, how can i retrieve my issueAmount-value from the hook and use it in Datagrid? (e.g. for filter and sort)

Related

Invalidating a cached query conditionally in React with useQueryClient hook

I am quite new to react and am struggling with a subtle problem with data fetching/re-fetching.
We have a data source with its own UI, which lets you create multiple discussion topics and is open to users. These topics can be updated with comments and other activities(deletion of comments/attachments/links) etc from the UI. This data source also exposes endpoints that list all topics currently in the system and the details of user activities on each of them.
Our UI, which is a React app talks to these end points and:
lists out the topics.
upon clicking on one of the items shows the activity counts on the item.(in a separate panel with two tabs - one for comment and one for other user activities)
I am responsible for number 2 above.
I wrote a custom hook to achieve this goal which is called by the panel and used the useQueryClient to invalidate my query inside the hook, but unfortunately, every time the component(panel) re-renders or I switch between the tabs a new call is made to fetch the count which is deemed unnecessary. Instead we want the call to fetch the counts to go out only when the user clicks on the item and the panel opens up. But I am unable to achieve this without violating the rules of hooks(calling it inside conditionals/ calling it outside of a react component).
export const useTopicActivityCounts = (
topicId: string | undefined,
): ITopicActivityCounts | undefined => {
useQueryClient().invalidateQueries(['TopicActivitytCounts', { topicId }]);
const { data } = useQuery(
['TopicActivityCounts', { topicId }],
() =>
fetchAsync<IResult<ITopicActivityCount>>(endpointUrl, {
method: 'GET',
params: {
id,
},
}).then(resp => resp?.value),
{
enabled: !!topicId,
staleTime: Infinity,
},
);
return data;
this hook is called from here:
export const TopicDetails = memo(({ item, setItem }: ITopicDetails): JSX.Element => {
const counts = useTopicActivityCounts(item?.id);
const headerContent = (
<Stack>
/* page content */
</Stack>
);
const items = [
/* page content */,
];
const pivotItems = [
{
itemKey: 'Tab1',
headerText: localize('Resx.Comms', { count: counts?.commentsCount ?? '0' }),
},
{
itemKey: 'Tab2',
headerText: localize('Resx.Activities', { count: counts?.activitiesCount ?? '0' }),
},
];
return (
/*
page content
*/
);
});
I have tried placing it inside an onSuccess inside the hook and that did not work.

Getting an Empty array in props

I am trying to make table using react-table in which one of the columns will be a toggle-switch in typescript. I am using the following code to create a react-switch.
const tableInstance = useTable({
columns,
data},(hooks)=> {
console.log("setting buildings: ",data);
hooks.visibleColumns.push((columns) => columns.map((column)=>column.id === "cevacrunningstatus"?{...column,Cell:({row})=>
{return <BuildingStartStopSwitch row_={row} data__={buildings} setdata={setbuildings}/>}}:column));
}
)
I am using the following React function component
interface props{
row_:Row.Row<any>;
data__:BuildingControlStatus[];
setdata: React.Dispatch<React.SetStateAction<BuildingControlStatus[]>>;
}
const BuildingStartStopSwitch:React.FC<props> = ({row_,data__,setdata}) => {
const [state,setState] = useState<boolean>(row_.values.runningstatus);
const handleChange = (checked:boolean) => {
setState(checked);
console.log("Data before statechange: ",data__)
setdata(data__.map((data_)=>row_.values.ccid === data_.ccid?({...data_,runningstatus:checked}):data_))
}
console.log("Data after statechange: ",data__)
return (
<Switch onChange={handleChange} checked={state}/>
);
};
export default BuildingStartStopSwitch;
I have the following issue:
The array data__ is turning up as an empty array inside BuildingStartStopSwitch. The data variable which is assigned to data__ contains items, but the same is not reflected inside BuildingStartStopSwitch. I am trying to update the data variable(which is the table data) to reflect the status of toggle switch.
I have an input cell against each toggle switch in a row. When the switch is checked, the input should be enabled and when the switch is unchecked , it should be disabled. I am not sure how to implement.
Thanks in advance!

react ant design table with json - updating data without key problem

I would like to display some json data (coming from my backend and handled in a hook) in a similar way to this : https://ant.design/components/table/#components-table-demo-tree-data
I have a column with a checkbox and a column with an input (both must be editable by the user) and the final json state must be updated with the new datas.
Here you can find the structure of my json : https://pastebin.com/wA0GCs1K
Here you have a screen of the final result :
The code I used to fetch the data :
const [dataServiceInterface, setDataServiceInterface] = useState(null);
useEffect(() => {
CreateApiService.fetchDataServiceInterface(questionFiles, responseFiles).then((result) => {
if (result != null) {
setDataServiceInterface(result);
}
});
}, []);
Here you have the code I used to update the attributes (column constant for the second part) :
const onInputChange = (key, record, index) => (e) => {
e.preventDefault();
console.log(key);
console.log(index);
console.log(record);
console.log(e.target.value);
dataServiceInterface.itemsQ[index][key] = e.target.value;
};
// some other code here
{
title: "Json Name",
dataIndex: "name",
key: "name",
render: (text, record, index) => (
<Input
//defaultValue={text}
value={text}
onChange={onInputChange("name", record, index)}
/>
),
},
Problem is (I think) : As I dont have a key defined in my json datas (parents and children), when I try to update the children it dosent work. I can't change the structure of the json because it's a business constraint. I was thinking of copying my json data in another state, then add the keys ... and still didn't try this solution and don't know if it works. I will update this if it's the case.
Meanwhile, if someone had the same issue and has any idea/hint/suggestion, would appreciate very much. Thx.
I think the problem here is that the component is not rendering the data once you have it. This can be solved by using useState hook, you should lookup for the docs.
I would love to get a little bit more of the component that you are building. But the approach to this would be something like this:
const Component = () {
const [data, useData] = useState([]);
const onInputChange = (key, record, index) => (e) => {
e.preventDefault();
const data = // You get fetch the data here
useData(data);
};
return <>
{
data && data.itemsQ.map((item) => {
// here you display the information contained in each item
})
}
</>
}

AG grid react framwork component cell render doesn't updae props

I am building a react functional component with an AgGridReact:
const DataGrid = (props) =>
{
const [gridRowData, setGridRowData] = useState([]);
const [columnDefinition, setColumnDefinition] = useState([]);
useEffect(async () =>
{
if(props.name && props.name !== "")
{
... get grid row data and column definition here according to props.name - working fine
}
},[props.name]);
let frameworkComponents = {
customLoadingOverlay: LoadingOverlayTemplate,
customNoRowsOverlay: UxDataGridCustomNoRows,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting} />
};
.
.
.
const onDeleteSetting = async () =>
{
console.log("ON DELETE AND NAME IS: " + props.name ); //PRINTS AN EMPTY STRING
...
}
return (
<AgGridReact
columnDefs={columnDefinition}
rowData={gridRowData}
frameworkComponents={frameworkComponents}/>
);
As you can see in the comment in onDeleteSetting, the name is empty when this callback is invoked. The rendering of the cell itself is fine and ViewAndDeleteSetting is indeed rendered, just it seems like it is being rendered only once and not every time the name changes. How can I cause the inner ViewAndDeleteSetting component to be re rendered with the most recent frameworkComponents?
The problem was with how I tried passing props to ViewAndDeleteSetting. If you want to pass prop to a cell rendered component, you shouldn't be doing it in frameworkComponents, but rather you need to do it in the column definition like this:
useEffect(() =>
{
let columns = [{headerName: '', cellRenderer: 'editButton', width: 90, editable: false,
cellRendererParams: {
openAddConfigurationsWindow: openAddConfigurationsWindow,
onDeleteSetting: onDeleteSetting
}},
.. other columns
]
setColumnDefinition(columns);
},[props.name]);
The columns with the cellRendererParams do gets recreated in the useEffect when the name changes, and then the component can access this params regularly via its props

Iterating over a specific row of a react ag-grid

I got an ag-grid with a button rendered in a dedicated column (meaning each row has a button on the side under that column). I've done it like this:
const columnDefinition = [
{headerName: '', cellRenderer: 'editButton'},
{headerName: "Key", field: "Key"},
{headerName: "Value", field: "Value"}];
const overlayParams = {
frameworkComponents: {
editButton: EditMagnifierButton, //EditMagnifierButton is a react functional component which renders a button
}
return (
<Ag-Grid-React
rowData={myRowData}
columnDefs={columnDefinition}
{...overlayParams} />
);
I want to know how to iterate over the row where the user clicked the button and get all the values in each column of the row so i can pass them as props to another component.
The EditMagnifierButton:
const EditMagnifier = (props) =>
{
return (
<IconButton iconSvg={search} />
)
}
As mentioned in ag-Grid docs, ag-Grid passes some default props to each cell-renderer and these props are in the format of ICellRendererParams,
interface ICellRendererParams {
value: any, // value to be rendered
valueFormatted: any, // value to be rendered formatted
getValue: () => any, // convenience function to get most recent up to date value
setValue: (value: any) => void, // convenience to set the value
formatValue: (value: any) => any, // convenience to format a value using the column's formatter
data: any, // the row's data
node: RowNode, // row node
colDef: ColDef, // the cell's column definition
column: Column, // the cell's column
rowIndex: number, // the current index of the row (this changes after filter and sort)
api: GridApi, // the grid API
eGridCell: HTMLElement, // the grid's cell, a DOM div element
eParentOfValue: HTMLElement, // the parent DOM item for the cell renderer, same as eGridCell unless using checkbox selection
columnApi: ColumnApi, // grid column API
context: any, // the grid's context
refreshCell: () => void // convenience function to refresh the cell
}
Detail can be found under,
https://www.ag-grid.com/javascript-grid-cell-rendering-components/#cell-renderer-component
So, there is one property called data which points to the rowData pointing to the index where the cell-renderer is present (in this case, the clicked one).
So your cell-renderer can use this prop directly as props.data,
/* Here the props will point to ICellRendererParams */
const EditMagnifier = (props) =>
{
const printRowData = (event) => {
/* This will give you the complete row data for the clicked row*/
console.log(props.data);
}
return (
<IconButton iconSvg={search} onClick={printRowData}/>
)
}

Resources