Error: Objects are not valid as a React child (found: object with keys {low, high})

Viewed 184

I'm pretty new to coding and this is my first post on Stack Overflow. I'm trying to build a React app. Here is the repo: https://github.com/Amaro-Koberle/k-net/

Here is a hosted version of the project (very early stages): https://quizzical-edison-523c55.netlify.app/

There are two components that cause an issue: NodeDisplay.js and EditNode.js. Both of those components render a list of all incoming and outgoing links (the app is a graph visualisation, when you click a node, a side-bar is supposed to display the content of the node and a list of all links that connect to it). I know that the issue is with this list, because when I comment the corresponding part out, things start working again. Whenever React tries to render either of those lists, the app crashes and I get this error: Error: Objects are not valid as a React child (found: object with keys {low, high}). If you meant to render a collection of children, use an array instead.

NodeDisplay.js (the part that causes that the problem is currently commented out, hence you can click on a node in the hosted app without it crashing)

import { MdEdit } from "react-icons/md";
import { uuid } from "uuidv4";

export default function NodeDisplay({ currNode, setEditing }) {
  if (currNode.id === "") {
    console.log("No node is currently selected");
    return null;
  }

  return (
    <div className="mt-4">
      <div className="flex items-center space-x-2 text-lg">
        <h3>{currNode.title}</h3>
        <button className="edit-button" onClick={() => setEditing(true)}>
          <MdEdit></MdEdit>
        </button>
      </div>
      <div className="mt-4">
        <p className="text-sm">{currNode.description}</p>
        <div>
          <div className="mt-4">
            <h5>Incoming Links</h5>
            {/* <ul>
              {currNode.inLinks.map((link) => (
                <li key={uuid}>{link}</li>
              ))}
            </ul>
          </div>
          <div className="mt-4">
            <h5>Outgoing Links</h5>
            <ul>
              {currNode.outLinks.map((link) => (
                <li key={uuid}>{link}</li>
              ))}
            </ul> */}
          </div>
        </div>
      </div>
    </div>
  );
}

EditNode.js (here, the list isn't commented out, so the app still does crash when you try to edit a node (by clicking the little pencil icon)

import React from "react";
import { useState } from "react";
import { MdClose } from "react-icons/md";
import { uuid } from "uuidv4";

export default function EditNode({
  currNode,
  setCurrNode,
  setEditing,
  updateGraph,
  createLink,
  removeLink,
}) {
  const [sourceInput, setSourceInput] = useState("");
  const [targetInput, setTargetInput] = useState("");

  return (
    <div className="mt-4">
      <div className="flex items-center space-x-2 text-lg">
        <>
          <h3>Edit Node</h3>
        </>
        <>
          <button onClick={() => setEditing(false)}>
            <MdClose></MdClose>
          </button>
        </>
      </div>
      <form className="mt-4">
        {/* title and description */}
        <>
          <label className="label" htmlFor="title">
            Title
          </label>
          <>
            <input
              className="w-full input"
              type="text"
              id="title"
              placeholder="Title"
              value={currNode.title}
              onInput={(e) =>
                setCurrNode({ ...currNode, title: e.target.value })
              }
            ></input>
          </>
          <>
            <label className="label" htmlFor="description">
              Description
            </label>
            <>
              <textarea
                className="w-full input"
                rows="5"
                id="description"
                placeholder="Description"
                value={currNode.description}
                onInput={(e) =>
                  setCurrNode({ ...currNode, description: e.target.value })
                }
              ></textarea>
            </>
          </>
        </>
        {/* links */}
        <>
          {/* incoming links */}
          <div className="mt-4">
            <h4>Incoming Links</h4>
            <>
              <label className="label" htmlFor="createInLink">
                Source
              </label>
              <div className="inline-flex space-x-1">
                <input
                  className="input"
                  type="text"
                  id="createInLink"
                  placeholder="Source node ID"
                  value={sourceInput}
                  onInput={(e) => setSourceInput(e.target.value)}
                ></input>
                <>
                  <button
                    className="btn"
                    type="button"
                    onClick={() => createLink(sourceInput, currNode.id)}
                  >
                    Connect
                  </button>
                </>
              </div>
            </>
            <ul>
              {currNode.inLinks.map((link) => {
                return (
                  <li key={uuid()}>
                    <span>{link}</span>
                    <button
                      className="btn"
                      type="button"
                      onClick={() => removeLink(link, currNode.id)}
                    >
                      Remove
                    </button>
                  </li>
                );
              })}
            </ul>
          </div>
          {/* outgoing links */}
          <div className="mt-4">
            <h4>Outgoing Links</h4>
            <>
              <label className="label" htmlFor="createOutLink">
                Target
              </label>
              <div className="inline-flex space-x-1">
                <>
                  <input
                    className="input"
                    type="text"
                    id="createOutLink"
                    placeholder="Target node ID"
                    value={targetInput}
                    onInput={(e) => setTargetInput(e.target.value)}
                  ></input>
                </>
                <>
                  <button
                    className="btn"
                    type="button"
                    onClick={() => createLink(currNode.id, targetInput)}
                  >
                    Connect
                  </button>
                </>
              </div>
            </>
            <ul>
              {currNode.outLinks.map((link) => {
                return (
                  <li key={uuid()}>
                    <span>{link}</span>
                    <button
                      className="btn"
                      type="button"
                      onClick={() => removeLink(currNode.id, link)}
                    >
                      Remove
                    </button>
                  </li>
                );
              })}
            </ul>
          </div>
          {/* <button className="btn" type="button" onClick={updateGraph}>
            Save
          </button> */}
        </>
      </form>
    </div>
  );
}

There are other Stack Overflow questions in regards to this error, but reading the answers didn't help because I still don't understand the issue and I don't understand what changes are needed to fix it. I hope I'm not being a nuisance by asking again, in regards to my specific case.

Any help would be greatly appreciated.

1 Answers

In my case, changing <span>{link}</span> to <span>{link.low}</span> got rid of the error because that points react directly to the child.

What was confusing was that link wasn't supposed to have any properties called low or high. The hose came from some issue with h the javascript-driver of neo4j, which doesn't handle integers well. In the back-end, had to change

const nodes = nodesResult.records.map((r) => {
    return {
      id: r.get("id").toNumber(),
      inLinks: r.get("inLinks"),
      outLinks: r.get("outLinks"),
      title: r.get("title"),
      description: r.get("description"),
    };
  });

to

const nodes = nodesResult.records.map((r) => {
    return {
      id: r.get("id").toNumber(),
      inLinks: r.get("inLinks").map((inLink) => inLink.toNumber()),
      outLinks: r.get("outLinks").map((outLink) => outLink.toNumber()),
      title: r.get("title"),
      description: r.get("description"),
    };
  });

Notice I added .map((inLink) => inLink.toNumber()) and .map((outLink) => outLink.toNumber()).

That fixed the issue, the data arriving at the front-end is now what I expected it to be (namely and array for inLinks and an array for outside Links, instead of an object with h keys high: and low:.

I hope this helps if anyone else runs into this issue.

Related