Je viens de commencer à apprendre React
Je travaille sur une solution où je veux créer un arbre comme un tableau en utilisant react.
En gros, la fonctionnalité est la suivante : il y a un tableau simple avec chaque ligne ayant une icône d'expansion conditionnelle, j'ai réalisé la fonctionnalité de base de rendre un autre composant de ligne lorsque je clique sur la ligne.
La solution de base que j'ai réalisée est la suivante : chaque fois que l'utilisateur clique sur une ligne, j'appelle la fonction expand row, j'ajoute les enfants statiques sous le tableau de la ligne cliquée et je les affiche en passant l'objet children au composant subrow.
Maintenant, je veux que chaque fois que l'utilisateur clique sur les enfants de la rangée étendue, elle doit s'étendre au niveau suivant en affichant les données liées à la deuxième extension.
Donc, en gros, cela ressemblera à
- Row 1
- Child 1
- Child 1.1
- Child 1.2
+ Child 2
+ Row 2
J'ai créé un prototype de base utilisant des données json statiques à partir d'apis jsonplaceholder.
Voici le 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
Voici l'implémentation de codesandbox Table imbriquée
Je ne veux pas utiliser de paquets externes pour cela. Veuillez m'aider.
Ajout d'un scénario conditionnel d'expansion et d'effondrement
Je veux faire une expansion conditionnelle basée sur le tableau que je maintiens.
Disons que j'ai un tableau splits [a,b,c], si la valeur est fixée à ce tableau, la ligne de premier niveau aura des données liées à A. Maintenant, chaque fois que l'utilisateur clique sur B, je vais faire une requête AJAX avec les données de la ligne de A et afficher les lignes de B avec une icône d'expansion car ce sera un tableau arborescent à 3 niveaux, de même chaque fois que l'utilisateur clique sur C, je vais envoyer les données de b et récupérer les données de C. Maintenant, D n'aura pas d'expansion supplémentaire car la taille du tableau est de 3, chaque fois que l'utilisateur ajoute un 4e élément dans le tableau, je dois voir l'icône d'expansion sur 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);