Just started using react-table, trying to figure out how to conditionally render something based on the accessor value. I get back some data from an api call and use one of the values for the accessor.
{
Header: "Action",
id: "startTime",
accessor: "attributes.startTime"
}
So I have a column with header "Action", here I want to conditionally render a button if the accessor value attrbiutes.startTime === null or something along those lines.
Rendering of the UI occurs in a different file so I also need to access it there for handling button onClick
I have a codesandbox here with a working example.
You can use custom cell renderer
const columns = [
{
Header: "Action",
id: "startTime",
accessor: "attributes.startTime",
Cell: props => {
return props.value === null ? <button>Click Me </button> : props.value;
}
}
];
Related
I've taken over a React project and I'm fairly new to the library. I'm attempting to implement AG-Grid and what I think is a relatively simple custom header that renders the column name and an icon. When the icon is clicked, I need the icon to change and update the state of the custom header components parent.
My issue is that the click event works once and updates the state in the parent, but the state doesn't propagate back down to the custom header component and therefore the icon doesn't switch. Not sure if this is an AG Grid issue or a React issue.
My Custom Header Component
const WeightHeaderComponent = (params) => {
const handleClick = () => {
params.weightModeCallback()
}
return (
<span onClick={handleClick} style={{cursor: 'pointer'}}>
Weight<i className={`${params.weightModeIsPercent ? 'fa fa-percent cn-blue' : 'fa fa-tally cn-blue'}`} style={{marginLeft: '3px'}}></i>
</span>
);
};
The AG Grid column def using the custom header component:
const [columnDefs] = useState([
...
{
headerName: 'Weight',
field: 'weight',
type: 'numericColumn',
headerTooltip:
'The weight of an assignment category determines the impact an assignment will have over your overall grade.',
width: 100,
defaultMinWidth: 100,
headerComponentParams: { weightModeIsPercent: weightModeIsPercent, weightModeCallback: updateWeightMode },
headerComponent: WeightHeaderComponent
},
...
])
The relevant state hook:
const [weightModeIsPercent, setWeightModeIsPercent] = useState(true);
And finally my callback function in the parent
function updateWeightMode () {
setWeightModeIsPercent(!weightModeIsPercent);
}
After an hour of so of doc reading and head-desk beating I figured it out. I was attempting to do this in a very roundabout way when AG Grid offers a nice little api which happens to have some functions for exactly this type of thing: updating your columnDefs.
Specifically: setColumnDefs(newDefs).
My code accepts an updated parameter with which I update the columnDefs for the column I want to update.
function updateWeightMode (updatedWeightModeBool) {
const newDefs = columnDefs.map((def) => {
if (def.field === 'weight') {
return {
...def,
headerComponentParams: {
weightModeIsPercent: updatedWeightModeBool,
weightModeCallback: updateWeightMode
}
};
}
return def;
});
weightsGridRef.current.api.setColumnDefs(newDefs);
}
So, this wasn't really an issue with state (though I'm still updating the parents state for other uses).
Try using
api.refreshHeader();
I have my array and I have no problem getting what I need using the accessorFn... but in the cell render I'm trying to populate a variable that is in another object and not in the data array used to by the react-table.
const defaultColumns = [
{
accessorKey: "Traveler",
header: "Traveler",
accessorFn: (row) => `${row.LTAGNAME} ${row.FTAGNAME} (${row.EMPNUM})`,
cell: (props) => (
<Link to={`/${group.ID}/registrant/${props.row.original.id}`}>
{props.getValue()}
</Link>
),
size: 290,
},
{group.ID} is a separate object in React unrelated to the data being passed into the useReactTable function. Is it possible to reference something outside of the "data" ?
I have created a custom table component I forked from ant design. I reuse it in all my components, It takes an array of all columns and renders it. I pass columns as a prop called initialColumns.
My issue is whenever the user changes the language, the table contents is re rendering but not the columns which I passed, they don't get translated, How would I force a rerender when the language is changed.
custom table component
const TableComponent = (props) => {
const { initialColumns, dataSource, handleClick } = props
return ( <Table
columns={colmenu.visibleColumns}
dataSource={dataSource}
size="small"
pagination={{
pageSizeOptions: ['10', '20', '50'],
showSizeChanger: true,
}}
/>)
}
Parent component, here I call my TableComponent as pass it columns
It looks something like this:
const columns = [
{
title: t.status,
dataIndex: 'status',
key: 'status',
sorter: (a, b) => a.status.localeCompare(b.status),
...GetColumnSearchProps(['status']),
className: 'text-center',
checked: true,
},
.
.
.
.
here is how I get the translated files
const { messages: t } = useIntl()
and this is the render method:
<TableComponent
initialColumns={columns}
dataSource={data}
handleClick={addModal}
title="AABC"
/>
So how would I update the initialColumns prop when the language is changed?
I need to have a row action only in certain rows (with particular property values). For example, if I have a row that has the property isDeletable set to true, I would like to be able to delete it, i.e have a delete icon present in the actions column.
Thanks in advance!
In actions definitions of your MaterialTable component, you can access to rowData parameter which you can be used to conditionally calculate the disabled or hidden props of each action. Check the following example where the action enabled only when status ==='active'.
<MaterialTable
// ..other props
actions={[
(rowData) => {
return {
icon: "bug_report",
tooltip: "Report bug",
disabled: rowData.status === "active",
// hidden: rowData.status === "active",
onClick: (event, rowData) =>
alert("This client status is " + rowData.status)
};
}
]}
/>
Here is a sandbox whit a working example.
Let me know if that worked for you!
In my React Project I have one component that has 2 child components
Search Form
Employee Projects List (loads only once)
I would like to load employee projects only once on page load. Then every time search form values change I would like to filter the table data. The problem is whenever I change search inputs, it causes too many re-renders error
Too many re-renders. React limits the number of renders to prevent an infinite loop.
I have checked other solutions, but nothing helped me to figure out this. Please checkout the sample in CodeSandBox
It re-renders because you are passing through props the search form state, and when you change that state (typing in the search form) the component re-renders and execute all the if statement you have inside it, so it run's the setData function triggering another re-render because you change the internal state of that component and then start an infinite loop of renders because every time it enters to the if statements it runs the setData function with a different array argument (remember JavaScript treats all none basic types by references so every time data.filter() executes, it may return same array a similar array but its other reference, so for JavaScript its not the same array).
To fix this you can put all if statements inside other useEffect hook like this:
useEffect(() => {
// If statemenst
if (
props.formik.values.projectName !== undefined &&
props.formik.values.projectName !== null &&
props.formik.values.projectName !== ""
) {
setData(data.filter((employeeProject) => employeeProject.projectName));
}
if (
props.formik.values.projectType !== undefined &&
props.formik.values.projectType !== null &&
props.formik.values.projectType !== ""
) {
setData(data.filter((employeeProject) => employeeProject.projectType));
}
}, [props.formik.values.projectName, props.formik.values.projectType])
Aldo this code fix the error, will not work as intended because, it will filter the list, and when you get an empty list there is no way it fill again, so the grid will remain empty until you refresh the page manually, so you should filter the list on the JSX without changing the state/list like:
export default function EmployeeProjects(props: any) {
const [data, setData] = useState<EmployeeProject[]>([]);
useEffect(() => {
let employeeProjects: EmployeeProject[] = [];
employeeProjects.push(new EmployeeProject(1, "React", "Frontend"));
employeeProjects.push(new EmployeeProject(2, "Angular", "Frontend"));
employeeProjects.push(new EmployeeProject(3, "Vue", "Frontend"));
employeeProjects.push(new EmployeeProject(4, "Java", "Backend"));
employeeProjects.push(new EmployeeProject(5, "C#", "Backend"));
employeeProjects.push(new EmployeeProject(6, "Python", "Backend"));
setData(employeeProjects);
}, []);
return (
<div style={{ marginTop: "20px" }}>
<DataGrid
autoHeight={true}
columns={[
{ field: "id", headerName: "ID", flex: 200 },
{ field: "projectName", headerName: "Project Name", flex: 200 },
{ field: "projectType", headerName: "Project Type", flex: 200 }
]}
rows={data.filter(
(employeeProject) =>
employeeProject.projectName.includes(
props.formik.values.projectName
) ||
employeeProject.projectName.includes(
props.formik.values.projectType
)
)}
/>
</div>
);
}