Single editable table row on add or edit - javascript

I have a react-table in my react app which renders rows of transactions. I have logic that adds a new, empty row on a button click. I want this row (and only this row) to be editable so that I can use it to create new transactions. I've tried to accomplish this using the Cell column property and the row index from the cellInfo. This works to make only the single column that I'm testing with editable, but the rest of the cells in that column are blank.
import React from 'react';
import axios from 'axios';
import ReactTable from "react-table";
import "react-table/react-table.css";
const API = 'http://127.0.0.1:8000/api/transactions/';
class Transactions extends React.Component {
constructor() {
super();
this.state = {
data: [],
isLoading: true,
error: null
};
this.fetchData = this.fetchData.bind(this);
this.handleAddTransaction = this.handleAddTransaction.bind(this);
this.renderEditable = this.renderEditable.bind(this);
}
handleAddTransaction() {
let newEmptyRow = {
date: "",
payee: "",
category: "",
amount: ""
}
const insert = (arr, index, newItem) => [ ...arr.slice(0, index), newItem, ...arr.slice(index) ];
console.log(newEmptyRow);
this.setState(currentState => ({ data: insert(currentState.data, 0, newEmptyRow) }));
}
fetchData(state, instance) {
this.setState({
isLoading: true
});
axios.get(API).then(response => {
this.setState({
data: response.data.results,
isLoading: false
});
});
}
renderEditable(cellInfo) {
if(cellInfo.row._index === 0) {
return (
<div
style={{ backgroundColor: "#009999" }}
contentEditable
suppressContentEditableWarning
onBlur={e => {
const data = [...this.state.data];
data[cellInfo.index][cellInfo.column.id] = e.target.innerHTML;
this.setState({ data });
}}
dangerouslySetInnerHTML={{
__html: this.state.data[cellInfo.index][cellInfo.column.id]
}}
/>
);
}
}
render() {
const {
data,
isLoading
} = this.state;
return (
<div>
<button onClick={this.handleAddTransaction} type="button">New +</button>
<div>
<ReactTable
columns = {[
{
Header: 'Date',
accessor: 'date',
width: 200,
Cell: this.renderEditable
},
{
Header: 'Payee',
accessor: 'payee',
width: 400
},
{
Header: 'Category',
accessor: 'category',
width: 200
},
{
Header: 'Amount',
accessor: 'amount',
width: 200
},
{
Header: 'Balance',
accessor: 'balance',
width: 200
}]}
data = {data}
onFetchData={this.fetchData} // Request new data when things change
defaultPageSize = {500}
sortable = {false}
manual
style={{
// This will force the table body to overflow and scroll
// TODO Finalize height
height: "800px"
}}
loading={isLoading}
className="-striped -highlight"
/>
</div>
</div>)
};
}
export default Transactions;

Related

React Error: "Rendered more hooks than during the previous render"

I have the following component. I used react hooks (useHistory, useState) in my component.
export default function ClassTheoryDataTable() {
const dataSource = [
{
key: '1',
date: '18.03.2021',
subject: 'Revision',
inst: 'HASHEL',
edit: 'edit',
delete: 'delete'
}
];
let history = useHistory();
const [tableData, setTableData] = useState(dataSource);
const handleRedirect = (data) => {
history.push("/ClassTheoryDetails");
};
const handleDelete = (key) => {
let dataSource1 = [...tableData];
dataSource1 = dataSource1.filter((item) => item.key !== key);
setTableData(dataSource1);
}
const columns = [
{
title: 'Date',
dataIndex: 'date',
key: 'date',
render: (text, record) => (
<a onClick={() => handleRedirect(record)} type="text">{text}</a>
)
},
{
title: 'Subject',
dataIndex: 'subject',
key: 'subject',
editable: true
},
{
title: 'Inst.',
dataIndex: 'inst',
key: 'inst',
editable: true
},
{
title: '',
dataIndex: 'edit',
key: 'edit',
width: '50px',
render: (text, record) => (
<Space size="middle">
<EditOutlined style={{ color: '#1589FF', fontSize: '15px' }} />
</Space>
)
},
{
title: '',
dataIndex: 'delete',
key: 'delete',
width: '50px',
render: (text, record) => (
dataSource.length >= 1 ?
<Popconfirm title="Sure to delete ?" onConfirm={() => handleDelete(record.key)}>
<CloseCircleFilled style={{ color: 'red', fontSize: '15px' }} />
</Popconfirm>
: null
)
}
];
return (
<>
<Table columns={columns} dataSource={tableData} pagination={false} bordered />
</>
);
}
Essentially I want to delete table row by clicking the delete icon in the last column. But, I'm getting the "Rendered more hooks than during the previous render" error when I load the page. I don't know how to fix it. Can someone help me?
The error is being thrown in the AntD component code, but is only manifesting because of how you've specified your table props.
The issue is in your ClassTheory component in the expandRowRender you are not instantiating the ClassTheoryDataTable subtable correctly.
const expandedRowRender = () => {
const table = new ClassTheoryDataTable();
return table;
};
Here you are invoking the function component directly and returning it, but this isn't how React components are instantiated. In React you describe the UI via JSX and pass this to React and React handles the entire component lifecycle, from instantiation, mounting, rerendering, and unmounting.
This function should return valid JSX.
const expandedRowRender = () => {
return <ClassTheoryDataTable />;
};

Error: Element type is invalid: expected a string in react ,when trying to update state

I got this error when updating state from fetch.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
This is my code below
import React from "react"
import useState from "react"
import {
Button,
Progress,
UncontrolledDropdown,
DropdownMenu,
DropdownToggle,
DropdownItem,
Input,
Badge,
Row,
Col,
ListGroup,
ListGroupItem
} from "reactstrap";
import Draggable from 'react-draggable'
import StatisticsCard from "../../components/#vuexy/statisticsCard/StatisticsCard"
import DataTable from "react-data-table-component"
import classnames from "classnames"
import ReactPaginate from "react-paginate"
import { history } from "../../history"
import {
Edit,
Trash,
ChevronDown,
Plus,
Check,
ChevronLeft,
ChevronRight,
Monitor,
UserCheck,
Mail,
Eye,
MessageSquare,
ShoppingBag,
Heart,
Smile,
Truck,
Cpu,
Server,
Activity,
AlertOctagon,
CreditCard,
Share,
Image
} from "react-feather"
import { connect } from "react-redux"
import {
getData,
getInitialData,
deleteData,
updateData,
addData,
filterData
} from "../../redux/actions/data-list/"
import Sidebar from "./DataListSidebar"
import Chip from "../../components/#vuexy/chips/ChipComponent"
import Checkbox from "../../components/#vuexy/checkbox/CheckboxesVuexy"
import "../../assets/scss/plugins/extensions/react-paginate.scss"
import "../../assets/scss/pages/data-list.scss"
import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import NumberFormat from 'react-number-format';
import $ from 'jquery'
import tc from 'thousands-counter';
class DataListConfig extends React.Component {
constructor(props){
super(props)
this.state = {
data: [],
campaigns: [],
tt: 0,
totalPages: 0,
currentPage: 0,
columns: [
{
name: "Name",
selector: "name",
sortable: true,
cell: row => (
<p title={row.name} className="text-truncate text-bold-500 mb-0">
{sessionStorage.Name}
</p>
)
},
{
name: "Title",
selector: "title",
sortable: true,
cell: row => (
<p className="text-bold-500 text-truncate mb-0">{row.title}</p>
)
},
{
name: "Total Donation",
selector: "totaldonor",
sortable: true,
cell: row => (
<p className="text-bold-500 text-truncate mb-0"> <NumberFormat value={row.amounts} displayType={'text'} thousandSeparator={true} prefix={'₦'} /></p>
)
},
{
name: "Status",
selector: "status",
sortable: true,
cell: row => (
<Badge
color={row.status === null || row.status === '' ? "light-danger" : "light-success"}
pill>
{row.status === null || row.status === '' ? "Pending":"Active"}
</Badge>
)
},
{
name: "Start Date",
selector: "startdate",
sortable: true,
cell: row => <p className="text-bold-500 mb-0">{row.startdate}</p>
},
{
name: "Category",
selector: "category",
sortable: true,
cell: row => <p className="text-bold-500 mb-0">{row.category}</p>
},
{
name: "Actions",
sortable: true,
cell: row => (
<this.ActionsComponent
row={row}
getData={this.props.getData}
parsedFilter={this.props.parsedFilter}
currentData={this.handleCurrentData}
deleteRow={this.handleDelete}
/>
)
}
],
allData: [],
value: "",
comments:[],
rowsPerPage: 4,
sidebar: false,
currentData: null,
selected: [],
totalRecords: 0,
sortIndex: [],
addNew: ""
}
const headers = { 'Content-Type': 'application/json' , 'Authorization':`Bearer ${sessionStorage.jwt}`}
fetch(' ', { headers })
.then(response => response.json())
.then(data => {
this.setState({ data: data.posts })
}).catch((error) => {
console.log(error)
});
}
componentDidMount()
{
function ActionsComponent(props) {
const {
buttonLabel,
className
} = props;
const [modal, setModal] = useState(false);
const [modal2, setModal2] = useState(false);
const [items, setItems] = useState([]);
const [comments,setCom] = useState([])
const Details = (id)=> {
$.post(' ',{
postid: id
},function(data,status){
setItems([
...items,
{
datas: data
}
]);
})
}
$.post(' ',{
postid: props.row.id
},function(data,status){
this.setState({commets: data})
})
let datas
datas = {totaldonors: "",totalcomments:""}
items.map((key)=>{
datas = key.datas
})
const dashdata = datas
const toggleModal = (id) =>{ setModal(!modal); };
const toggleModal2 = (id) =>{ setModal2(!modal2); };
return (
<div>
<Draggable>
<Modal
isOpen={modal}
toggle={toggleModal}
className="modal-dialog-centered"
backdrop={false}
>
<ModalHeader toggle={toggleModal}>
Campaign Dashboard!
</ModalHeader>
<ModalBody className="modal-dialog-centered">
<div className="container">
<div className="row">
<div className="col-sm">
<StatisticsCard
hideChart
iconBg="primary"
icon={<CreditCard className="primary" size={22} />}
stat={tc(dashdata.totaldonors)}
statTitle="Total Donors"
/>
</div>
<div className="col-sm">
<StatisticsCard
hideChart
iconBg="primary"
icon={<Share className="primary" size={22} />}
stat="36.9k"
statTitle="Total Shares"
/>
</div>
<div className="col-sm">
<StatisticsCard
hideChart
iconBg="primary"
icon={<MessageSquare className="primary" size={22} />}
stat={tc(dashdata.totalcomments)}
statTitle="Total Comments"
/>
</div>
<div className="container" style={{'max-height':'300px','overflow-y': 'scroll'}}>
</div>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button color="primary">
Request Withdrawal
</Button>{" "}
</ModalFooter>
</Modal>
</Draggable>
<div className="data-list-action">
{" "}
{" "}
<Edit
className="cursor-pointer mr-1"
size={20}
onClick={() => {
return props.currentData(props.row)
}}
/>
<Activity
className="cid cursor-pointer mr-1"
size={20}
onClick={()=>{return toggleModal(),Details(props.row.id);}}
/>
</div>
</div>
)
}
}
thumbView = this.props.thumbView
componentDidUpdate(prevProps, prevState) {
if (this.thumbView) {
this.thumbView = false
let columns = [
{
name: "Name",
selector: "name",
sortable: true,
cell: row => (
<p title={row.name} className="text-truncate text-bold-500 mb-0">
{sessionStorage.Name}
</p>
)
},
{
name: "Title",
selector: "title",
sortable: true,
cell: row => (
<p className="text-bold-500 text-truncate mb-0">{row.title}</p>
)
},
{
name: "Status",
selector: "status",
sortable: true,
cell: row => (
<Badge
color={row.status === null || row.status === '' ? "light-danger" : "light-success"}
pill>
{row.status === null || row.status === '' ? "Pending":"Active"}
</Badge>
)
},
{
name: "Total Donation",
selector: "totaldonor",
sortable: true,
cell: row => (
<p className="text-bold-500 text-truncate mb-0">{row.amounts}</p>
)
},
{
name: "Start Date",
selector: "startdate",
sortable: true,
cell: row => <p className="text-bold-500 mb-0">{row.startdate}</p>
},
{
name: "Category",
selector: "category",
sortable: true,
cell: row => <p className="text-bold-500 mb-0">{row.category}</p>
},
{
name: "Actions",
sortable: true,
cell: row => (
<this.ActionsComponent
row={row}
getData={this.props.getData}
parsedFilter={this.props.parsedFilter}
currentData={this.handleCurrentData}
deleteRow={this.handleDelete}
/>
)
}
]
this.setState({ columns })
}
}
handleFilter = e => {
this.setState({ value: e.target.value })
this.props.filterData(e.target.value)
}
handleRowsPerPage = value => {
let { parsedFilter, getData } = this.props
let page = parsedFilter.page !== undefined ? parsedFilter.page : 1
history.push(`/funding/?page=${page}&perPage=${value}`)
this.setState({ rowsPerPage: value })
getData({ page: parsedFilter.page, perPage: value })
}
handleSidebar = (boolean, addNew = false) => {
this.setState({ sidebar: boolean })
if (addNew === true) this.setState({ currentData: null, addNew: true })
}
handleDelete = row => {
this.props.deleteData(row)
this.props.getData(this.props.parsedFilter)
if (this.state.data.length - 1 === 0) {
let urlPrefix = this.props.thumbView
? "/funding/"
: "/funding/"
history.push(
`${urlPrefix}list-view?page=${parseInt(
this.props.parsedFilter.page - 1
)}&perPage=${this.props.parsedFilter.perPage}`
)
this.props.getData({
page: this.props.parsedFilter.page - 1,
perPage: this.props.parsedFilter.perPage
})
}
}
handleCurrentData = obj => {
this.setState({ currentData: obj })
this.handleSidebar(true)
}
handlePagination = page => {
let { parsedFilter, getData } = this.props
let perPage = parsedFilter.perPage !== undefined ? parsedFilter.perPage : 4
let urlPrefix = this.props.thumbView
? "/funding/b"
: "/funding/"
history.push(
`${urlPrefix}?page=${page.selected + 1}&perPage=${perPage}`
)
getData({ page: page.selected + 1, perPage: perPage })
this.setState({ currentPage: page.selected })
}
render() {
let {
columns,
data,
allData,
totalPages,
value,
rowsPerPage,
currentData,
sidebar,
totalRecords,
sortIndex
} = this.state
return (
<div
className={`data-list ${
this.props.thumbView ? "thumb-view" : "list-view"
}`}>
<CustomHeader
handleSidebar={this.handleSidebar}
handleFilter={this.handleFilter}
handleRowsPerPage={this.handleRowsPerPage}
rowsPerPage="5"
total={totalRecords}
index={sortIndex}
/>
<DataTable
columns={columns}
data={data}
pagination
noHeader
selectableRows
responsive
pointerOnHover
selectableRowsHighlight
onSelectedRowsChange={data =>
this.setState({ selected: data.selectedRows })
}
customStyles={selectedStyle}
subHeaderComponent={
<CustomHeader
handleSidebar={this.handleSidebar}
handleFilter={this.handleFilter}
handleRowsPerPage={this.handleRowsPerPage}
rowsPerPage="5"
total={totalRecords}
index={sortIndex}
/>
}
sortIcon={<ChevronDown />}
selectableRowsComponent={Checkbox}
selectableRowsComponentProps={{
color: "primary",
icon: <Check className="vx-icon" size={12} />,
label: "",
size: "sm"
}}
/>
<Sidebar
show={sidebar}
data={currentData}
updateData={this.props.updateData}
addData={this.props.addData}
handleSidebar={this.handleSidebar}
thumbView={this.props.thumbView}
getData={this.props.getData}
dataParams={this.props.parsedFilter}
addNew={this.state.addNew}
/>
<div
className={classnames("data-list-overlay", {
show: sidebar
})}
onClick={() => this.handleSidebar(false, true)}
/>
</div>
)
}
}
const mapStateToProps = state => {
return {
dataList: state.dataList
}
}
const handleSidebar = (boolean, addNew = false) => {
this.setState({ sidebar: boolean })
if (addNew === true) this.setState({ currentData: null, addNew: true })
}
export default connect(mapStateToProps, {
getData,
deleteData,
updateData,
addData,
getInitialData,
filterData
})(DataListConfig)
i removed my api endpoint for security purpose
bug image
The DataListConfig component tries to render <this.ActionsComponent>. Because ActionsComponent is defined inside the componentDidMount method, instead of at the class level, this.ActionsComponent is undefined. Which causes the error.
Ultimately though defining a component inside another component is a bad idea. Inside a class component's class as a field, it will mean that each instance of that class component will have their own version of the function component. This smells fishy at best, and is not very React-like.
Move ActionsComponent outside the DataListConfig component entirely, and pass in data it needs through its props.
function ActionsComponent(props) {
// ...
}
class DataListConfig extends React.Component {
// ...
}

Need to get all the values of all the child component instances and use it in parents method: ReactJS

I have this scenario where I have a table(react-table), where I am applying column level filtering. I have extracted this as a separate component(DropdDown Component) and this can be attached to any column . I am maintaining a method inside parent component which picks up the union of all the values i.e., selected values of all the dropdowns and then apply server side filtering.
Now the challenge here is , How can i get this consolidated values inside the parent component method?
This DropDown component has list of unique values with respect to that column, there is an Apply button , which applies the server side filtering. Now if I jump onto another column, I need to get the previously checked values and also the current values.
Inside handleSetData() filtering logic is written, I need to get the data from DropDown Component. Everytime I click on Apply on a column filter, I need to get the previously checked values as well.
Can someone help me with this:
Code Sandbox: https://codesandbox.io/s/quizzical-glitter-np8iw
App Component
import * as React from "react";
import { render } from "react-dom";
import ReactTable from "react-table";
import "./styles.css";
import "react-table/react-table.css";
import DropDownComponent from "./DropDown";
interface IState {
data: {}[];
columns: {}[];
}
interface IProps {}
export default class App extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
data: [
{ firstName: "aaaaa", status: "Pending", visits: 155 },
{ firstName: "aabFaa", status: "Pending", visits: 155 },
{ firstName: "adaAAaaa", status: "Approved", visits: 1785 },
{ firstName: "aAaaaa", status: "Approved", visits: 175 },
{ firstName: "adaSaaa", status: "Cancelled", visits: 165 },
{ firstName: "aaaaa", status: "Cancelled", visits: 157 },
{ firstName: "aaaaa", status: "Approved", visits: 153 },
{ firstName: "aaaaa", status: "Pending", visits: 155 }
],
columns: []
};
}
handleSetState = (columns: any) => {
this.setState({ columns });
};
handleSetData = (value: any) => {
console.log(value); // Here filtering logic is written, I need to get the data from DropDown Component. Everytime I click on Apply on a column filter, I need to get the previously checked values as well
};
componentDidMount() {
let columns = [
{
Header: () => (
<div>
<div style={{ position: "absolute", marginLeft: "10px" }}>
<DropDownComponent
data={this.state.data}
handleSetData={this.handleSetData}
param="firstName"
/>
</div>
<span>First Name</span>
</div>
),
accessor: "firstName",
sortable: false,
show: true,
displayValue: " First Name"
},
{
Header: () => (
<div>
<div style={{ position: "absolute", marginLeft: "10px" }}>
<DropDownComponent
data={this.state.data}
handleSetData={this.handleSetData}
param="status"
/>
</div>
<span>Status</span>
</div>
),
accessor: "status",
sortable: false,
show: true,
displayValue: " Status "
},
{
Header: "Visits",
accessor: "visits",
sortable: false,
show: true,
displayValue: " Visits "
}
];
this.setState({ columns });
}
render() {
const { data, columns } = this.state;
return (
<div>
<ReactTable
data={data}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
DropDown Component
import * as React from "react";
import { Button, Checkbox, Icon } from "semantic-ui-react";
interface IProps {
data: {}[];
handleSetData(arr: any): void;
param: string;
}
interface IState {
showList: boolean;
optionsArr: {}[];
originalState: {}[];
}
export default class DropDownComponent extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
showList: false,
optionsArr: [],
originalState: []
};
}
toggleList = () => {
this.setState(prevState => ({ showList: !prevState.showList }));
};
handleItemClick = (event: React.FormEvent<HTMLInputElement>, data: any) => {
const index = this.state.optionsArr.findIndex(
(item: any) => item.text === data.name
);
const optionsArr = this.state.optionsArr.map((prevState: any, i: any) =>
i === index
? {
key: prevState.key,
text: prevState.text,
checked: !prevState.checked
}
: prevState
);
this.setState({ optionsArr });
};
submitSelection = () => {
console.log(this.state.optionsArr.filter((item: any) => item.checked)); // This gives me selecte ones
let checkedValues: any = this.state.optionsArr.filter(
(item: any) => item.checked
);
this.setState({ originalState: this.state.optionsArr }, () =>
this.props.handleSetData(checkedValues)
);
};
componentDidMount() {
if (this.props.data) {
let arr = this.props.data;
let uniqueValues = Array.from(
new Set(arr.map((arr: any) => arr[this.props.param]))
);
var optionsArr = [];
for (let i = 0; i < uniqueValues.length; i++) {
var options: any = {};
options["key"] = uniqueValues[i];
options["text"] = uniqueValues[i];
options["checked"] = false;
optionsArr.push(options);
}
this.setState({ optionsArr: optionsArr, originalState: optionsArr });
}
}
clearSelection = (event: any) => {
// Push it to previous state, before cancel was clicked
this.setState({ showList: false, optionsArr: this.state.originalState });
};
render() {
let { showList } = this.state;
let visibleFlag: string;
if (showList === true) visibleFlag = "visible";
else visibleFlag = "";
return (
<div>
<div style={{ position: "absolute" }}>
<div
className={
"ui scrolling dropdown column-settings customized " +
visibleFlag +
" " +
this.props.menuDirection
}
>
<Icon className="filter" onClick={this.toggleList} />
{this.state.optionsArr.length > 0 ? (
<>
<div className="menu-item-holder">
{this.state.optionsArr.map((item: any, i: number) => (
<div className="menu-item" key={i}>
<Checkbox
name={item.text}
onChange={this.handleItemClick}
checked={item.checked}
label={item.text}
/>
</div>
))}
</div>
<div className="menu-btn-holder">
<Button size="small" onClick={this.submitSelection}>
Apply
</Button>
<Button size="small" onClick={this.clearSelection}>
Cancel
</Button>
</div>
</>
) : (
""
)}
</div>
</div>
</div>
</div>
);
}
}
I have modified your DropDown component. I make it receive a list of items to generate the checkboxes. Instead of sending the whole data object to the DropDown component, I think it makes more sense send a ready list to them, the Main component should generate the right data structure (I haven't done that, you have to create those functions). In the component, I create three states to manage the component.
Obs: I removed typescript to make faster for me.
condesandbox

change reference of this keyword after importing file

I am using react-table to display the data. I want to keep my table column outside the react component since it is too big to put in there and reusability purpose as well. I made a table config to hold all my table config. The problem is I am using some of my local state reference in columns. So when I import column from config file it gives error where I have used this.state.
tableconfig.js
export const table = [
{
resizable: false,
width: 50,
Cell: ({ original }) => {
const { pageActivity, pageNumber } = this.state;
const currentPageActivityRow = {
...get(pageActivity[pageNumber], "selectedRows")
};
return (
<Checkbox
checked={currentPageActivityRow[original.id] === true}
onChange={() => this.selectRows(original.id)}
/>
);
}
},
{
Header: "Contact ID",
accessor: "id"
}
];
tableComponent.js
import {table} from '../config';
constructor(props){
super(props);
this.state = {
pageNumber: 1,
search: "",
error: "",
dialogOpen: false,
isFilerActive: false,
selectedFilters: {},
pageActivity: {}
};
this.columns = table
}
maybe try binding your table? It has to be a function though
const table = () => (
[
{
resizable: false,
width: 50,
Cell: ({ original }) => {
const { pageActivity, pageNumber } = this.state;
const currentPageActivityRow = {
...get(pageActivity[pageNumber], "selectedRows")
};
return (
<Checkbox
checked={currentPageActivityRow[original.id] === true}
onChange={() => this.selectRows(original.id)}
/>
);
}
},
{
Header: "Contact ID",
accessor: "id"
}
];
)
// tableComponent.js
constructor(props){
super(props);
this.state = {
pageNumber: 1,
search: "",
error: "",
dialogOpen: false,
isFilerActive: false,
selectedFilters: {},
pageActivity: {}
};
this.columns = table.bind(this);
}

Antd filtering not working correctly, client code is executed but no filtering happeans

I am trying to follow this page sample:
https://ant.design/components/table/
The antd filtering sample to be precise
I have a column which I know its 2 possible values only.
I can see in the debugger that the handlechange event is executed, but after click OK in the filter, the table is not filtered as it should
My best guess I am missing something on the OnFilter event
import React, { Component } from 'react';
import { Table, Tag, Button} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
class ListPageTemplatesWithSelection extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
filteredInfo: null,
sortedInfo: null,
};
this.handleChange= this.handleChange.bind(this);
this.clearFilters= this.clearFilters.bind(this);
this.clearAll= this.clearAll.bind(this);
}
handleChange(pagination, filters, sorter){
console.log('Various parameters', pagination, filters, sorter);
this.setState({
filteredInfo: filters,
sortedInfo: sorter,
});
}
clearFilters(){
this.setState({ filteredInfo: null });
}
clearAll(){
this.setState({
filteredInfo: null,
sortedInfo: null,
});
}
fetchData = () => {
adalApiFetch(fetch, "/PageTemplates", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
const results= responseJson.map(row => ({
key: row.Id,
Name: row.Name,
SiteType: row.SiteType,
Tags: row.Tags
}))
this.setState({ data: results });
}
})
.catch(error => {
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render(){
let { sortedInfo, filteredInfo } = this.state;
sortedInfo = sortedInfo || {};
filteredInfo = filteredInfo || {};
const columns = [
{
title: 'Id',
dataIndex: 'key',
key: 'key',
},
{
title: 'Name',
dataIndex: 'Name',
key: 'Name',
},
{
title: 'Site Type',
dataIndex: 'SiteType',
key: 'SiteType',
filters: [
{ text: 'Modern Team Site', value: 'Modern Team Site' },
{ text: 'CommunicationSite', value: 'CommunicationSite' },
],
filteredValue: filteredInfo.name || null,
onFilter: (value, record) => record.Tags.includes(value),
},{
title: 'Tags',
key: 'Tags',
dataIndex: 'Tags',
render: Tags => (
<span>
{Tags && Tags.map(tag => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return <Tag color={color} key={tag}>{tag.toUpperCase()}</Tag>;
})}
</span>
),
}
];
const rowSelection = {
selectedRowKeys: this.props.selectedRows,
onChange: (selectedRowKeys) => {
this.props.onRowSelect(selectedRowKeys);
}
};
return (
<div>
<Button onClick={this.clearFilters}>Clear filters</Button>
<Button onClick={this.clearAll}>Clear filters and sorters</Button>
<Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} onChange={this.handleChange} />
</div>
);
}
}
export default ListPageTemplatesWithSelection;
In your SiteType column, you have mistakenly set filteredValue prop to filteredInfo.name. But the filter is not on a name column, it is on SiteType column.
Change this line from:
filteredValue: filteredInfo.name || null,
To:
filteredValue: filteredInfo.SiteType || null,
And it should be fine.
You need execute the fetchData function in the handleChange function. Add filter params to ajax request. Just like this:
handleTableChange = (pagination, filters, sorter) => {
const pager = { ...this.state.pagination };
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
}

Categories

Resources