0

I'm trying to build a TreeNode function in react that builds a tree structure of some data.

I have fake data as a starting point:

const fakeData = {
 "1234": {
   id: "1234",
   name: "Division",
   address: "string",
   childNodes: []
  }
};

When I expand the "Division" node with the "id" 1234, I want to fetch the children of this node. So my fakeData expands to something like this:

I'm using the fetch() method to get data, but I'm unsure of how to pass that data to resolve() method of the Promise. Right now I'm doing this:

function fakeAjax(url, id) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(res => res.json())
      .then(result => {
        const nodes = result.map(node => node.id);
        resolve(fakeData[id].childNodes.map(nodes));
      });
  });
}

function fetchChildNodes(id) {
  return fakeAjax(`someUrl`, id);
}

function TreeNode({ id, name }) {
  const [childNodes, setChildNodes] = useState(null);

  // Toggle our display of child nodes
  const toggleExpanded = useCallback(() => {
      fetchChildNodes(id)
        .then(nodes => setChildNodes(nodes.map(node => <TreeNode {...node} />)))
    }
  }, [childNodes]);

But this is not passing the relavant data to the resolve method. Am I doing something wrong?

John
  • 447
  • 1
  • 8
  • 22

1 Answers1

1

A couple of things jump out:

  1. Your code is using the explicit promise creation anti-pattern. fetch returns a promise, there's no need to wrap new Promise around it. It's not the problem, but it's a problem, not least because the way fakeAjax is written, if the ajax call fails, the promise returned by fakeAjax is never rejected (and never fulfilled, it just stays pending forever).

  2. You're calling map passing in a non-function as the mapping function:

    const nodes = result.map(node => node.id);
    resolve(fakeData[id].childNodes.map(nodes));
    // Here −−−−−−−−−−−−−−−−−−−−−−−−−−−−^
    

    That doesn't make any sense. map accepts a function, not an array.

  3. Your code is getting hit by the footgun in the fetch API that almost everyone else is also hit by: You need to check that the request worked at the HTTP level. fetch only rejects when there's a network error, not an HTTP error (more on my blog here).

  4. I assume your ajax call returns actual node data that you should be passing directly to TreeNode, not modifying (and certainly not turning into an array of just id values).

So something like:

function fetchJSON(url) {
    return fetch(url)
      .then(res => {
          if (!res.ok) {
              throw new Error("HTTP error " + res.status);
          }
          return res.json();
      });
}

function fetchChildNodes(id) {
    return fetchJSON(`someUrl`, id);
}

The mapping of node data to TreeNode is happening in TreeNode, so I don't think you need to do anything in fetchChildNodes.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875