Home > database >  React component renders before the data is successfully fetched from the API so it can't render
React component renders before the data is successfully fetched from the API so it can't render

Time:10-27

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

  • Related