I'm trying display tree data using MUI TreeView component. I have an object passed to the RenderTreeWithCheckboxes function. If I declare global my variable data and pass in function RenderTreeWithCheckboxes then my code works fine. But if I do fetch data from API and setState for my variable my code doesn't work. I think the problem is because the component is rendered before my data is successfully fetched. Here is my code
I also have code in CodeSanBox: https://codesandbox.io/s/quizzical-feather-oyg9f0?file=/src/App.js
import { useEffect, useState } from "react";
import "./styles.css";
import axios from "axios";
import TreeView from "@mui/lab/TreeView";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import TreeItem from "@mui/lab/TreeItem";
import { Checkbox, FormControlLabel } from "@mui/material";
const category = {
id: "0",
termName: "Tất cả chuyên mục",
subTermTaxonomies: [
{
id: 1,
termName: "Không có chuyên mục",
description: "Không có chuyên mục",
subTermTaxonomies: []
},
{
id: 2,
termName: "Tin tức chung",
description: "Chuyên mục Tin tức chung",
subTermTaxonomies: []
},
{
id: 3,
termName: "Hướng nghiệp",
description: "Chuyên mục Hướng nghiệp",
subTermTaxonomies: []
},
{
id: 4,
termName: "Tin tuyển sinh",
description: "Chuyên mục Tin tuyển sinh",
subTermTaxonomies: []
},
{
id: 5,
termName: "Đại học FPT",
description: "Trường Đại học FPT",
subTermTaxonomies: [
{
id: 6,
termName: "Đại học FPT - Hà Nội",
description: "Đại học FPT - Hà Nội",
subTermTaxonomies: []
},
{
id: 7,
termName: "Đại học FPT - Đà Nẵng",
description: "Đại học FPT - Đà Nẵng",
subTermTaxonomies: []
},
{
id: 8,
termName: "Đại học FPT - TP. Hồ Chí Minh",
description: "Đại học FPT - TP. Hồ Chí Minh",
subTermTaxonomies: []
},
{
id: 9,
termName: "Đai học FPT - Cần Thơ",
description: "Đai học FPT - Cần Thơ",
subTermTaxonomies: []
},
{
id: 10,
termName: "Đai học FPT - Quy Nhơn",
description: "Đai học FPT - Quy Nhơn",
subTermTaxonomies: []
}
]
}
]
};
export default function App() {
const [category, setCategory] = useState({});
const [selected, setSelected] = useState([]);
function getChildById(node, id) {
let array = [];
//returns an array of nodes ids: clicked node id and all children node ids
function getAllChild(nodes) {
if (nodes === null) return [];
array.push(nodes.id);
if (Array.isArray(nodes.subTermTaxonomies)) {
nodes.subTermTaxonomies.forEach((node) => {
array = [...array, ...getAllChild(node)];
array = array.filter((v, i) => array.indexOf(v) === i);
});
}
return array;
}
//returns the node object that was selected
function getNodeById(nodes, id) {
if (nodes.id === id) {
return nodes;
} else if (Array.isArray(nodes.subTermTaxonomies)) {
let result = null;
nodes.subTermTaxonomies.forEach((node) => {
if (!!getNodeById(node, id)) {
result = getNodeById(node, id);
}
});
return result;
}
return null;
}
return getAllChild(getNodeById(node, id));
}
function getOnChange(checked, nodes) {
//gets all freshly selected or unselected nodes
const allNode = getChildById(category, nodes.id);
//combines newly selected nodes with existing selection
//or filters out newly deselected nodes from existing selection
let array = checked
? [...selected, ...allNode]
: selected.filter((value) => !allNode.includes(value));
setSelected(array);
}
const RenderTreeWithCheckboxes = (nodes) => {
return (
<TreeItem
key={nodes.id}
nodeId={nodes.id.toString()}
label={
<FormControlLabel
control={
<Checkbox
checked={selected.some((item) => item === nodes.id)}
onChange={(event) =>
getOnChange(event.currentTarget.checked, nodes)
}
//onClick={(e) => e.stopPropagation()}
/>
}
label={<>{nodes.termName}</>}
key={nodes.id}
/>
}
>
{Array.isArray(nodes.subTermTaxonomies)
? nodes.subTermTaxonomies.map((node) =>
RenderTreeWithCheckboxes(node)
)
: null}
</TreeItem>
);
};
const fetchCategoryHandler = async () => {
const response = await axios.get(
`https://news-feamsapi.tranduydat.com/api/term-taxonomy/get-ref`,
{
headers: {
Authorization: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmYmE3OTdjNC1hZGRmLTQ0MDEtOTIzOC01NDNlZTMxNWI5MTAiLCJlbWFpbCI6ImR1Y2xvbmcya3p6QGdtYWlsLmNvbSIsIm5hbWUiOiJidWkgZHVjIGxvbmciLCJqdGkiOiJhYTFhMGVlYi02NGE3LTRiMTAtYjgxMi01OGU1N2RhMTQ1NDgiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJVc2VyIiwiZXhwIjoxNjY5MTEzMzI3LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMDEiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.i-G56xKTM432C7D1xcVJHJXkktOCxG3KdmU8WdhcyZs`
}
}
);
const data = response.data.data;
const transformData = { id: "0", termName: "", subTermTaxonomies: [] };
transformData.subTermTaxonomies = data;
setCategory(transformData);
};
useEffect(() => {
fetchCategoryHandler();
console.log("category", category);
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<TreeView
name="category"
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpanded={["0"]}
defaultExpandIcon={<ChevronRightIcon />}
>
{RenderTreeWithCheckboxes(category)}
</TreeView>
</div>
);
}
I have learned to use useEffect and do fetch data inside useEffect but still everything is not working. I want to render my component with data fetch from API, not hardcode
CodePudding user response:
Need to add a condition to check nodes?.id
exists
{nodes?.id && (
<TreeItem
.....
</TreeItem>
)}
</>
https://codesandbox.io/s/compassionate-sound-3s5n2j?file=/src/App.js:2052-2924