I am using react-table for data-grid purposes. I have extracted react-table as a separate component where-in I just pass necessary props to it and it renders the grid.
I am trying to get the info related to a particular row whenever I click on it. I am trying getTrProps but does not seem like working.
Sandbox: https://codesandbox.io/s/react-table-row-table-g3kd5
App Component
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
interface IProps {}
interface IState {
data: {}[];
columns: {}[];
}
class App extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
data: [],
columns: []
};
}
componentDidMount() {
this.getData();
}
getData = () => {
let data = [
{ firstName: "Jack", status: "Submitted", age: "14" },
{ firstName: "Simon", status: "Pending", age: "15" },
{ firstName: "Pete", status: "Approved", age: "17" }
];
this.setState({ data }, () => this.getColumns());
};
getColumns = () => {
let columns = [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Age",
accessor: "age"
}
];
this.setState({ columns });
};
onClickRow = () => {
console.log("test");
};
render() {
return (
<>
<DataGrid
data={this.state.data}
columns={this.state.columns}
rowClicked={this.onClickRow}
/>
</>
);
}
}
render(<App />, document.getElementById("root"));
DataGrid Component
import * as React from "react";
import ReactTable from "react-table";
import "react-table/react-table.css";
interface IProps {
data: any;
columns: any;
rowClicked(): void;
}
interface IState {}
export default class DataGrid extends React.Component<IProps, IState> {
onRowClick = (state: any, rowInfo: any, column: any, instance: any) => {
this.props.rowClicked();
};
render() {
return (
<>
<ReactTable
data={this.props.data}
columns={this.props.columns}
getTdProps={this.onRowClick}
/>
</>
);
}
}
Use this code to get info of a clicked row:
getTdProps={(state, rowInfo, column, instance) => {
return {
onClick: (e, handleOriginal) => {
console.log("row info:", rowInfo);
if (handleOriginal) {
handleOriginal();
}
}
}}}
You can check this CodeSandbox example: https://codesandbox.io/s/react-table-row-table-shehb?fontsize=14
you have quite a few errors in your code but to pass the value back you have to put it into your callback:
onRowClick = (state: any, rowInfo: any, column: any, instance: any) => {
this.props.rowClicked(rowInfo);
};
and read it out like this:
onClickRow = (rowInfo) => {
console.log(rowInfo);
};
Hope this helps.
Related
According to the API doc there needs to be a searchQuery prop which i've mentioned in my code but the search doesn't seem to be working
API doc doesn't explain how to implement it and the examples available online don't seem to be working on code sandbox.
The only article available which seems to explain search has incorrect code (duplicate props): https://frugalisminds.com/how-to-create-react-sortable-tree/
API Doc: https://www.npmjs.com/package/react-sortable-tree
Below is the code:
import React, { Component } from "react";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css";
export default class Tree extends Component {
constructor(props) {
super(props);
this.state = {
treeData: [
{ title: "Chicken", children: [{ title: "Egg" }] },
{ title: "Fish", children: [{ title: "fingerline" }] },
],
searchString: ""
};
}
handleSearchOnChange = e => {
this.setState({
searchString: e.target.value,
});
};
render() {
return (
<div style={{ height: 400 }}>
<input
type="search"
onChange={this.handleSearchOnChange}
className="form-control"
/>
<SortableTree
searchQuery={this.state.searchString}
treeData={this.state.treeData}
onChange={treeData => this.setState([...treeData])}
isVirtualized={false}
/>
</div>
);
}
}
missing a searchFocusOffset to highlight the found item and a searchMethod which can be custom defined inside render method as follows:
import React, { Component } from "react";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css"; // This only needs to be imported once in your app
export default class Tree extends Component {
constructor(props) {
super(props);
this.state = {
treeData: [
{ title: "Chicken", children: [{ title: "Egg" }] },
{ title: "Fish", children: [{ title: "fingerline" }] },
],
searchString: ""
};
}
render() {
// Case insensitive search of `node.title`
const customSearchMethod = ({ node, searchQuery }) =>
searchQuery &&
node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1;
return (
<div style={{ height: 400 }}>
<input
type="search"
onChange={event => this.setState({ searchString: event.target.value })}
className="form-control"
/>
<SortableTree
searchMethod={customSearchMethod}
searchQuery={this.state.searchString}
searchFocusOffset={0}
treeData={this.state.treeData}
onChange={treeData => this.setState([...treeData])}
isVirtualized={false}
/>
</div>
);
}
}
I've been trying to create a react-bootstrap-table2 but I get the following warning: Each child in a list should have a unique "key" prop.
Here is my code:
export const columns = [
{
dataField: "timestamps",
text: "Timestamp",
},
];
class Table extends Component {
constructor(props) {
super(props);
this.state = { timestamps: [] };
}
componentDidMount() {
const database = db.ref().child("timestamped_measures");
database.on("value", (ts_measures) => {
const timestamps = [];
const columns = [{ dataField: "timestamps", text: "Timestamp" }];
ts_measures.forEach((ts_measure) => {
timestamps.push(ts_measure.val().timestamp);
});
console.log(timestamps);
this.setState((prevState) => {
return { timestamps: [...prevState.timestamps, ...timestamps] };
});
});
}
render() {
return (
<div className="App">
<BootstrapTable
keyField="timestamps"
data={this.state.timestamps.map((item) => ({ item }))}
columns={columns}
pagination={paginationFactory()}
/>
</div>
);
}
}
export default Table;
Here is the console with the list of data I am trying to display
So my question is how to give each child in a list an unique key.
Any help would be appreciated!
You keyField should be set to dataField(key) not timestamps(value). Also, no mapping of data is required.
https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/table-props.html#keyfield-required-string
i.e.
<BootstrapTable
keyField="dataField"
data={this.state.timestamps}
columns={columns}
pagination={paginationFactory()}
/>
You are passing array of integers instead of array of objects that has "timestamp" property.
export const columns = [
{
dataField: "timestamp",
text: "Timestamp",
},
];
class Table extends Component {
constructor(props) {
super(props);
this.state = { timestamps: [] };
}
componentDidMount() {
const database = db.ref().child("timestamped_measures");
database.on("value", (ts_measures) => {
const timestamps = [];
ts_measures.forEach((ts_measure) => {
timestamps.push({ timestamp: ts_measure.val().timestamp });
});
console.log(timestamps);
this.setState((prevState) => {
return { timestamps: [...prevState.timestamps, ...timestamps] };
});
});
}
render() {
return (
<div className="App">
<BootstrapTable
keyField="timestamp"
data={this.state.timestamps}
columns={columns}
pagination={paginationFactory()}
/>
</div>
);
}
}
export default Table;
Basically I have a set of dynamic tables that are being displayed based on the values passed. If there is an empty array passed, it should show No data found. In my case when I send data to the table, all the tables will show "No data found" first then followed by the actual table content. I am not sure what is causing this.
The data is loaded asynchronously , it shows no data found and then the actual content. I have added setInterval to show this asynchronous nature
Sandbox:https://codesandbox.io/s/react-table-row-table-ryiny?file=/src/index.js:0-1322
Can someone help me?
Parent
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
const d1 = [{ name: "test", age: "20" }, { name: "test1", age: "15" }];
const d2 = [{ area: "area", pin: "123" }, { area: "area1", pin: "1245" }];
const c1 = [
{ Header: "Name", accessor: "name" },
{ Header: "Age", accessor: "age" }
];
const c2 = [
{ Header: "Area", accessor: "area" },
{ Header: "Pin", accessor: "pin" }
];
const d3 = [];
const c3 = [];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data1: [],
column1: [],
data2: [],
column2: [],
data3: [],
column3: []
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
data1: d1,
column1: c1
});
}, 2000);
setTimeout(() => {
this.setState({
data2: d2,
column2: c2
});
}, 2500);
this.setState({
data3: d3,
column3: c3
});
}
render() {
return (
<>
<DataGrid data={this.state.data1} columns={this.state.column1} />
<DataGrid data={this.state.data2} columns={this.state.column2} />
<DataGrid data={this.state.data3} columns={this.state.column3} />
</>
);
}
}
Child
import * as React from "react";
import ReactTable from "react-table";
import "react-table/react-table.css";
export default class DataGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
showMore: false
};
}
toggleState = () => {
this.setState(prevState => ({
showMore: !prevState.showMore
}));
};
formatData = () => {
let arr = [];
if (this.props.data && this.props.data.length > 0)
arr = this.state.showMore ? this.props.data : this.props.data.slice(0, 2);
return arr;
};
render() {
const { showMore } = this.state;
const { data, columns } = this.props;
const showLink = data.length > 2;
const subset = this.formatData();
return (
<>
{showLink && (
<button onClick={this.toggleState}>
Show {showMore ? "Less" : "More"}
</button>
)}
{data && data.length > 0 ? (
<ReactTable
showPagination={false}
data={subset}
columns={columns}
minRows={0}
NoDataComponent={() => null}
loading={false}
/>
) : (
"No data found"
)}
</>
);
}
}
Adding few points to the above answer.
The reason it was behaving in that way is not because of the asynchronous behavior but the life-cycle nature of the React component.which in this case takes place as:
The DataGrid is rendered with initial state of data i.e empty[] array.
No data is shown because empty[] array is passed in this cycle.
Then you are setting the state in componentDidMount.
To show the effect Datagrid is again re rendered with actual data.
Initialize the App state's data with null instead of an empty array (sandbox):
this.state = {
data1: null,
column1: [],
data2: null,
column2: [],
data3: null,
column3: []
};
In the DataGrid method check if the value is falsy (null counts, but empty array is truthy), and return null (nothing to render) if it is:
render() {
const { data, columns } = this.props;
if (!data) return null;
I have a situation where in I am having multiple on click events that are fired on a column header. There will be one event for filter dropdown and the other one for sorting. There is a filter icon , on click of which column filter options will be shown. And on click of the header, sorting should also happen.
Now whenever I click on the filter icon, both handlers are getting fired. Can someone help me with this.
On click of filter icon, only filter handler should fire
Help would be appreciated.
Sandbox: https://codesandbox.io/s/relaxed-feather-xrpkp
Parent
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 Child from "./Child";
interface IState {
data: {}[];
columns: {}[];
}
interface IProps {}
export default class App extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
data: [
{ firstName: "Jack", status: "Submitted", age: "14" },
{ firstName: "Simon", status: "Pending", age: "15" },
{ firstName: "Pete", status: "Approved", age: "17" }
],
columns: []
};
}
handleColumnFilter = (value: any) => {
console.log(value);
};
sortHandler = () => {
console.log("sort handler");
};
componentDidMount() {
let columns = [
{
Header: () => (
<div onClick={this.sortHandler}>
<div style={{ position: "absolute", marginLeft: "10px" }}>
<Child handleFilter={this.handleColumnFilter} />
</div>
<span>First Name</span>
</div>
),
accessor: "firstName",
sortable: false,
show: true,
displayValue: " First Name"
},
{
Header: () => (
<div onClick={this.sortHandler}>
<span>Status</span>
</div>
),
accessor: "status",
sortable: false
}
];
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);
Filter Component
import * as React from "react";
import { Icon } from "semantic-ui-react";
import "./styles.css";
interface IProps {
handleFilter(val1: any): void;
}
interface IState {
showList: boolean;
}
export default class Child extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
showList: false
};
}
toggleList = () => {
console.log("filter handler");
this.setState(prevState => ({ showList: !prevState.showList }));
};
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 " + visibleFlag}
>
<Icon className="filter" onClick={this.toggleList} />
</div>
</div>
</div>
);
}
}
You just need event.stopPropagation(). This will isolate the event to only this specific execution-block. So now when you click on the filter-icon, it will only trigger the designated event-handler.
toggleList = (event) => {
event.stopPropgation()
console.log("filter handler");
this.setState(prevState => ({ showList: !prevState.showList }));
};
You'll also need to use it here as well:
handleValueChange = (event: React.FormEvent<HTMLInputElement>, data: any) => {
event.stopPropagation()
let updated: any;
if (data.checked) {
updated = [...this.state.selected, data.name];
} else {
updated = this.state.selected.filter(v => v !== data.name);
}
this.setState({ selected: updated });
};
And here:
passSelectionToParent = (event: any) => {
event.preventDefault();
event.stopPropagation()
this.props.handleFilter(this.props.name, this.state.selected);
};
Literally, anytime you have a click-event for a parent-markup and it has children mark-up that also have a click-event, you can use event.stopPropagation() to stop the parent click-event from firing.
Here's the sandbox: https://codesandbox.io/s/elated-gauss-wgf3t
I'm currently passing data into my component via props, and for some reason it's showing up as undefined.
From my parent component perspective I have the following pass 2 props, data and header.
class ContractTable extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
render() {
return (
<div>
<p></p>
<MuiThemeProvider>
<TableTest
data={this.state.data}
header={[
{
name: "First Name",
prop: "firstName"
},
{
name: "Last Name",
prop: "lastName"
},
{
name: "Age",
prop: "age"
},
{
name: "Height",
prop: "height"
},
{
name: "Address",
prop: "address"
}
]}
/>
</MuiThemeProvider>
<p></p>
</div>
);
}
I try to grab the props and set it as my state, but when I log this.props.data or this.props.header it returns undefined. Why is this?
import React, { Component } from 'react';
import {
Table,
TableBody,
TableHeader,
TableHeaderColumn,
TableRow,
TableRowColumn,
} from 'material-ui/Table';
class TableTest extends Component {
constructor(props) {
super(props);
this.state = {
data: props.data,
header: props.header
}
this.row = this.row.bind(this);
}
row = (currentValue, index, header) => (
<TableRow key={`t-${index}`}>
{
header.map((headerName, index) => (
<TableRowColumn key={`trc-${index}`}>
{currentValue[headerName.prop]}
</TableRowColumn>
))
}
</TableRow>
);
render() {
return 'hello'
}
}
export default TableTest;
Update: take a look https://jsfiddle.net/nroLmghv/
Just rendered simple table header.
Passing props to state is not a good approach.
I created a snippet. And it looks working. Point in which place do you have a problem. Or provide MuiThemeProvider and TableTest full code.
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
// mock value
data: "some value"
}
}
render() {
return <div>
<TableTest
data={this.state.data}
header={[
{
name: "First Name",
prop: "firstName"
},
{
name: "Last Name",
prop: "lastName"
},
{
name: "Age",
prop: "age"
},
{
name: "Height",
prop: "height"
},
{
name: "Address",
prop: "address"
}
]}
/>
</div>;
}
}
class TableTest extends React.Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
header: this.props.header
}
console.log(this.state.data)
console.log(this.state.header)
}
render() {
return <div></div>;
}
}
ReactDOM.render(
<Example />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root">
</div>
Its an antipattern to set a state that is directly derivable from props, you would rather use the props directly. Also if you use the props to set state and you need to update state based on props change, you would also need to implement componentWillReceiveProps function