gpt4 book ai didi

reactjs - 如何使用递归使用react js创建树表?

转载 作者:行者123 更新时间:2023-12-04 15:05:30 25 4
gpt4 key购买 nike

我刚开始学习 React

我正在研究我想使用 React 创建树状表的解决方案。

所以基本上功能就像有一个简单的表格,每一行都有条件展开图标,我实现了当我点击行时渲染另一个行组件的基本功能。

我实现的基本解决方案是,每当用户单击任何我调用的函数展开行时,都会在该单击的行数组下添加静态子项,并使用将子对象传递给子行组件来显示它。

现在我希望每当用户点击展开的行子项时,它应该展开到下一级并显示与第二次展开相关的数据。

所以基本上它看起来像

- Row 1
- Child 1
- Child 1.1
- Child 1.2
+ Child 2
+ Row 2

我使用来自 jsonplaceholder api 的静态 json 数据创建了基本原型(prototype)

这是代码

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;

子行.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

这是codesandbox的实现Nested table

我不想为此使用任何外部包。请帮忙

添加条件展开折叠场景

我想根据我正在维护的数组进行扩展

假设我有一个数组拆分 [a,b,c],如果为此数组第一级行设置的值现在将包含与 A 相关的数据,每当使用点击 B 时,我将使用行数据发出 AJAX 请求A 的行并用展开图标显示 B 的行,因为这将是 3 级树表,类似地,每当用户单击 C 时,我都会发送 b 的数据并检索 C 的数据。现在 D 将不会进一步展开,因为数组大小为 3 ,每当用户在数组中添加第 4 个元素时,我必须在 C 上看到展开图标。

尝试 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);

最佳答案

解决方案一

import React, { useRef, useState, useEffect } from "react";
import axios from 'axios';
import "./style.css";

function ExpandableRow({
loadData = new Promise(resolve => resolve([])),
onExpand,
columns,
id,
row
}) {
/* added condition to test if not first render
* if yes then added data through reference which
* will update the state but because state is
* updated directly render would not occur
* to handle this issue `onExpand` function
* is passed from parent to child which will update
* state with new data which will intern cause rendering
*/
const [rowState, setRowState] = useState(1);
const didMount = useRef(false);

useEffect(() => {
if (didMount.current) {
if (row.hasOwnProperty("children")) {
delete row["children"];
onExpand("remove", id, row);
} else {
setRowState(2);
loadData(id, row).then(data => {
row['children'] = data;
onExpand("add", id, row);
setRowState(3);
});
}
} else didMount.current = true;
}, [rowState]);

return (
<tr>
<td>
<button
type="button"
onClick={() => {
setRowState(rowState == 3 ? 1 : 3);
}}
>
{rowState == 2 ? "o" : rowState == 3 ? '-' : "+"}
</button>
</td>

{columns.map((column, idx) => (
<td key={idx}>{row[column]}</td>
))}
</tr>
);
}

// function for recursively creating table rows
function ExpandableRowTable({ loadData, onExpand, columns, rows }) {
return rows.map((row, idx) => {
const actions = {
loadData,
onExpand
};

return (
<React.Fragment>
<ExpandableRow
id={idx}
columns={columns}
row={row}
{...actions}
/>
{row.children && (
<ExpandableRowTable
columns={columns}
rows={row.children}
{...actions}
/>
)}
</React.Fragment>
);
});
}

function apiData() {
return axios.get("https://jsonplaceholder.typicode.com/users")
.then((res) => {
return res.data;
});
}

let count = 0;
const columns = ["id", "name"];
export default function App() {
const [rows, setRows] = useState([]);

useEffect(() => {
apiData().then((data) => {
count = data.length;
setRows(data)
})
.catch(err => {
console.log(err);
})
}, []);

return (
<div>
<table>
<thead>
<tr>
<th />
{columns.map((c, idx) => (
<th key={idx}>{c}</th>
))}
</tr>
</thead>
<tbody>
<ExpandableRowTable
loadData={(id, row) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{
id: count + 1,
name: `- ${row["name"]}`
},
{
id: count + 2,
name: `- ${row["name"]}`
}
]);
count += 2
}, 1000);
})
}}
onExpand={(action, id, row) => {
if (action == "remove") {
count -= 2
}
setRows([...rows]);
}}
columns={columns}
rows={rows}
/>
</tbody>
</table>
</div>
);
}

解决方案 1 有效 example

解决方案2

import React, {
createContext,
useContext,
useRef,
useState,
useReducer,
useEffect
} from "react";
import axios from "axios";
import "./style.css";

const data = {
rows: [
{
id: 1,
name: "leanne graham"
},
{
id: 2,
name: "ervin howell"
},
{
id: 3,
name: "clementine bauch"
}
],
options: {
"": null,
cities: [
{
id: 1,
name: "delhi"
},
{
id: 2,
name: "pune"
},
{
id: 3,
name: "hyderabad"
}
],
network: [
{
id: 1,
name: "unknown"
},
{
id: 2,
name: "known"
},
{
id: 3,
name: "KNOWN"
}
]
}
};

const initialState = {
showIcon: false,
selection: [],
rows: []
};

const store = createContext(initialState);
const { Provider, Consumer } = store;

const StateProvider = ({ children }) => {
const [state, dispatch] = useReducer((state, action) => {
// console.log(state.rows);
switch (action.type) {
case "ADD_SELECTION":
if (state.selection.indexOf(action.payload) == -1) {
const newState = {...state};
newState['selection'].push(action.payload);
return newState;
}
return state;
case "SET_ROWS":
return Object.assign({}, state, {
rows: action.payload
});
case "TOGGLE_ICON":
return Object.assign({}, state, {
showIcon: !state.showIcon
});
default:
return state;
}
}, initialState);

return (
<Provider
value={{
state,
dispatch,
toggleIcon: () => {
dispatch({
type: "TOGGLE_ICON"
});
},
addSelection: value => {
dispatch({
type: "ADD_SELECTION",
payload: value
});
},
setRows: rows => {
dispatch({
type: "SET_ROWS",
payload: rows
});
}
}}
>
{children}
</Provider>
);
};
/*
1 - Loading (o)
2 - Plus (+)
3 - Minus (-)
*/
function ExpandableRow({
showIcon,
loadData = new Promise(resolve => resolve([])),
onExpand,
columns,
id,
row
}) {
/* added condition to test if not first render
* if yes then added data through reference which
* will update the state but because state is
* updated directly render would not occur
* to handle this issue `onExpand` function
* is passed from parent to child which will update
* state with new data which will intern cause rendering
*/
const [rowState, setRowState] = useState(2);
const didMount = useRef(false);

useEffect(() => {
if (didMount.current) {
if (row.hasOwnProperty("children")) {
if (rowState == 2) {
delete row["children"];
onExpand("remove", id, row);
}
} else {
setRowState(1);
didMount.current = false;
loadData(id, row).then(_data => {
row["children"] = _data;
onExpand("add", id, row);
setRowState(3);
});
}
} else didMount.current = true;
}, [rowState]);

return (
<tr>
<td>
{showIcon && (
<button
type="button"
onClick={() => {
setRowState(rowState == 3 ? 2 : 3);
}}
>
{rowState == 1 ? "o" : rowState == 3 ? "-" : "+"}
</button>
)}
</td>

{columns.map((column, idx) => (
<td key={idx}>{row[column]}</td>
))}
</tr>
);
}

// function for recursively creating table rows
function ExpandableRowTable({ showIcon, loadData, onExpand, columns, rows }) {
return rows.map((row, idx) => {
const actions = {
loadData,
onExpand
};

return (
<React.Fragment key={idx}>
<ExpandableRow
id={idx}
showIcon={showIcon}
columns={columns}
row={row}
{...actions}
/>
{row.children && (
<ExpandableRowTable
showIcon={showIcon}
columns={columns}
rows={row.children}
{...actions}
/>
)}
</React.Fragment>
);
});
}

function apiData(requestData) {
console.log(requestData);
return axios.get("https://jsonplaceholder.typicode.com/users").then(res => {
return res.data;
});
}

const columns = ["id", "name"];
function Select(props) {
const { state, toggleIcon, addSelection } = useContext(store);
const { selection } = state;
return (
<div>
<div>{selection.join(", ")}</div>
<select
value={""}
onChange={evt => {
selection[selection.length - 1] != evt.target.value && toggleIcon();
addSelection(evt.target.value);
}}
>
{Object.keys(data.options).map((c, idx) => (
<option key={idx}>{c}</option>
))}
</select>
</div>
);
}

function Table(props) {
const { state, toggleIcon, setRows } = useContext(store);
const { rows, selection } = state;

useEffect(() => {
// apiData().then((data) => {
setRows(data.rows);
// })
// .catch(err => {
// console.log(err);
// })
}, []);

return (
<table>
<thead>
<tr>
<th />
{columns.map((c, idx) => (
<th key={idx}>{c}</th>
))}
</tr>
</thead>
<tbody>
<ExpandableRowTable
showIcon={state.showIcon}
loadData={(id, row) => {
return new Promise(resolve => {
const lastSelection = selection[selection.length - 1];
setTimeout(() => {
resolve(data.options[lastSelection]);
}, 1000);
});
}}
onExpand={(action, id, row) => {
setRows(rows);
toggleIcon();
}}
columns={columns}
rows={rows}
/>
</tbody>
</table>
);
}

export default function App() {
return (
<div>
<StateProvider>
<Select />
<Table />
</StateProvider>
</div>
);
}

解决方案 2 有效 example

解决方案3

import React, { useState, useEffect, useRef, Fragment } from "react";
import axios from "axios";

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 = () => {
this.setState(
{
data: [...this.state.data]
},
() => {}
)
}

const ExpandableTableRow = ({ rows }) => {
const expandRow = (row) => {
row.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
}
];
row.isExpanded = true;
updateState();
};

const collapseRow = (row) => {
delete row.children;
row.isExpanded = false;
updateState();
};

const ExpandCollapsToggle = ({ row, expandRow, collapseRow }) => {
return (
<button type="button" onClick={() => row.isExpanded ? collapseRow(row) : expandRow(row)}>
{row.isExpanded ? '-' : '+'}
</button>
);
};

if (rows) {
return rows.map((row) => {
let children = null;
return (
<Fragment key={row.id}>
<tr key={row.id}>
<td>
<ExpandCollapsToggle
row={row}
expandRow={expandRow}
collapseRow={collapseRow}
/>{" "}
{row.id}
</td>
<td>{row.name}</td>
</tr>
<ExpandableTableRow rows={row.children} />
</Fragment>
);
});
return null;
};

if (this.state.data) {
return (
<Fragment>
<ExpandableTableRow rows={this.state.data} />
</Fragment>
);
}
return null;
}
}
const mapStateToProps = (state) => {
return { data: state.reportReducer.data };
};
export default TableRowData;

解决方案 3 有效 example

关于reactjs - 如何使用递归使用react js创建树表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66239269/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com