I have just started learning React
I am working on solution where i want to create tree like table using react.
So basically functionality is like there is simple table with each row having conditional expand icon, i achieved basic functionality of rendering another row component when i click on row.
Basic solution that i have achieved is whenever user clicks on any i call function expand row add static children under that clicked row array and display it using passing children object to subrow component.
Now i want whenever user clicks on expanded row children, it should expand to next level having displaying data related to second expand.
So basically it will look like
- Row 1
- Child 1
- Child 1.1
- Child 1.2
+ Child 2
+ Row 2
I have created basic prototype using static json data from jsonplaceholder apis
Here is code
App.js
import React, { useState, Fragment } from "react";
import TableRowData from "./TableRowData";
import "./styles.css";
export default function App() {
const [splits, setSplit] = useState(["campid", "appid", "os"]);
return (
<Fragment>
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<TableRowData avaliableSplits={splits} />
</tbody>
</table>
</Fragment>
);
}
TableRowData.js
import React, { useState, useEffect, useRef, Fragment } from "react";
import axios from "axios";
import SubRow from "./SubRow";
class TableRowData extends React.Component {
state = { data: [] };
constructor(props) {
super(props);
}
componentDidMount() {
axios.get("https://jsonplaceholder.typicode.com/users").then((res) => {
this.setState({ data: res.data });
});
}
render() {
const updateState = (id, itemAttributes) => {
var index = this.state.data.findIndex((x) => x.id === id);
if (index !== -1) {
this.setState({
data: [
...this.state.data.slice(0, index),
Object.assign({}, this.state.data[index], itemAttributes),
...this.state.data.slice(index + 1)
]
});
}
};
const expandRow = (user) => {
user.children = [
{ id: "6656", name: "sfsdfds1" },
{ id: "66563", name: "sfsdfds2" }
];
// this.setState({data:[...this.state.data],})
updateState(user.id, { isExpanded: true });
};
const collapseRow = (user) => {
user.children = undefined;
updateState(user.id, { isExpanded: false });
};
if (this.state.data) {
const appData = this.state.data.map((user) => {
return (
<Fragment key={user.id}>
<tr key={user.id}>
<td>
{user.isExpanded === true ? (
<button type="button" onClick={() => collapseRow(user)}>
-
</button>
) : (
<button type="button" onClick={() => expandRow(user)}>
+
</button>
)}
{user.id}
</td>
<td>{user.name}</td>
</tr>
{!!user.children && <SubRow rowData={user.children} />}
</Fragment>
);
});
return <Fragment>{appData}</Fragment>;
} else {
return null;
}
}
}
export default TableRowData;
SubRow.js
import React, { useState, useEffect, useRef, Fragment } from 'react';
import axios from 'axios';
const SubRow = (props) => {
const appData = props.rowData.map((user) => {
user.isExpanded = false;
return (
<Fragment key={user.id}>
<tr key={user.id}>
<td><button type='button' onClick={()=>handleClick(user,props.reportData)}>+</button>{user.id}</td>
<td>{user.name}</td>
</tr>
{!!user.children && <SubRow rowData={user.children} />}
</Fragment>
)
});
return (
<Fragment>{appData}</Fragment>
)
}
export default SubRow
Here is codesandbox implementation Nested table
I do not want to use any external packages for same. Please help
Adding conditional expand collapse scenario
I want to make expand conditional based on array i am maintaining
Lets say i have an array splits [a,b,c], if value set to this array first level row will have data related to A now whenever use clicks on B, i will make an AJAX request with row-data of A and display rows of B with expand icon as this will be 3 level tree table, similarly whenever user clicks on C i will send data of b and retrieve data of C. now D will not have any further expand as array size is 3, whenever user adds 4th element in array i have to saw expand icon on C.
Attemp 1:
import React, { useState, useEffect, useRef, Fragment } from "react";
import _ from 'lodash';
import axios from "axios";
import {connect} from 'react-redux';
import {getChildren} from '@src/redux/actions/reports';
class TableRowData extends React.Component {
state = {
showIcon: false,
selection: [],
data: []
};
constructor(props) {
super(props);
}
componentDidMount() {
axios.get("https://jsonplaceholder.typicode.com/users").then((res) => {
const rowData = res.data.map((row) => {
row.isExpanded = false;
return row;
});
this.setState({ data: rowData });
});
}
render() {
const updateState = (id, itemAttributes) => {
var index = this.state.data.findIndex((x) => x.id === id);
if (index !== -1) {
this.setState({
data: [
...this.state.data.slice(0, index),
Object.assign({}, this.state.data[index], itemAttributes),
...this.state.data.slice(index + 1)
]
});
}
};
const expandRow = (row) => {
const index = _(this.state.data)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.findIndex({ id: row.id });
if (index !== -1) {
let prevState = [...this.state.data];
let el = _(prevState)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.find({ id: row.id });
el.children = [
{ id: '_' + Math.random().toString(36).substr(2, 5), name: row.id+"_ID1", isExpanded:false,parentId:row.id },
{ id: '_' + Math.random().toString(36).substr(2, 5), name: row.id+"_ID2ß",isExpanded:false,parentId:row.id },
];
el.isExpanded=true;
this.setState({data:[...this.state.data],prevState},()=>{})
}
};
const collapseRow = (user) => {
delete user.children
// updateState(user.id, { children: {id:1,name:'JANAK'} });
updateState(user.id, { isExpanded: false });
};
const ExpandableTableRow = ({rows}) => {
//console.log(rows);
if (rows) {
return rows.map((row) => {
let children = null;
return (
<Fragment key={row.id}>
<tr key={row.id}>
<td>
<ExpandCollapsToggle row={row} /> {row.id}
</td>
<td>{row.name}</td>
</tr>
<ExpandableTableRow rows={row.children} />
</Fragment>
)
}
);
} else {
return null;
}
};
const ExpandCollapsToggle = ({row,actions}) => {
if(row.isExpanded === true) {
return (<button type="button" onClick={() => collapseRow(row)}>-</button>)
} else {
return (<button type="button" onClick={() => expandRow(row)}>+</button>)
}
}
if (this.state.data) {
return (
<Fragment>
<ExpandableTableRow rows={this.state.data} />
</Fragment>
);
} else {
return null;
}
}
}
const mapStateToProps = (state) => {
return {"data":state.reportReducer.data};
// return state;
}
export default connect(mapStateToProps,{getChildren})(TableRowData);