John Rogerson
John Rogerson

Reputation: 1183

Table Body in Semantic UI React with No Table Rows Causing validateDomNesting Error

I've gone through all of the example questions on this and can't seem to figure out what my problem is.

Here is the full error:

index.js:1375 Warning: validateDOMNesting(...): Text nodes cannot appear as a child of <tbody>.
    in tbody (created by TableBody)
    in TableBody (at Favorites.js:167)
    in table (created by Table)
    in Table (at Favorites.js:123)
    in Favorites (created by ConnectFunction)
    in ConnectFunction (at App.js:73)
    in Route (at App.js:69)
    in Router (created by BrowserRouter)
    in BrowserRouter (at App.js:46)
    in div (created by Container)
    in Container (at App.js:44)
    in App (created by ConnectFunction)
    in ConnectFunction (at src/index.js:10)
    in Provider (at src/index.js:9)

and my full code

import React, { useState, useEffect } from "react";
import Search from "./Search";
import { connect } from "react-redux";
import {
  Table,
  Popup,
  Responsive,
  Button,
  Segment,
  Header,
  Image
} from "semantic-ui-react";
import { searchChange } from "../reducers/searchReducer";
import { fetchData } from "../reducers/baseballReducer";
import { removeFavorite } from "../reducers/favoriteReducer";
import { getFavorites } from "../reducers/favoriteReducer";
import { getUpdates } from "../reducers/updateReducer";
import { setNotification } from "../reducers/notificationReducer";
import _ from "lodash";
var moment = require("moment");
moment().format();

//Filter 'favupdates' state for user's input
const searchCards = ({ favUpdates, search }) => {
  return search
    ? favUpdates.filter(a =>
        a.title
          .toString()
          .toLowerCase()
          .includes(search.toLowerCase())
      )
    : favUpdates;
};

const style = {
  borderRadius: 0,
  padding: "2em"
};

const Favorites = props => {
  useEffect(() => {
    document.title = "My Favorites | All Vintage Search";
  }, []);

  useEffect(() => {
    props.getFavorites(props.loggedUser.id);
  }, [props.loggedUser]);

  //Set 'filteredData' state
  useEffect(() => {
    setData(props.cardsToShow);
  }, [props]);

  const mapFAVS = props.favorites;
  const data = Array.from(mapFAVS);
  const updatedFavs = data.map(item => item.id);
  const formatFavs = updatedFavs.map(id => id.join(","));
  console.log("FORMAT FAVS", formatFavs);

  //Get updated data from eBay based on user's favorite id's and update 'favUpdates' state
  useEffect(() => {
    props.getUpdates(formatFavs);
  }, [props.favorites]);

  const [column, setColumn] = useState(null);
  const [direction, setDirection] = useState(null);
  const [filteredData, setData] = useState(props.cardsToShow);

  console.log("Filtered Data", filteredData);

  const handleSortNumeric = clickedColumn => {
    const sorter = data => parseInt(data[clickedColumn]);
    setData(_.sortBy(filteredData, sorter));
  };

  const handleSortReverse = () => {
    const sorter = data => parseInt(data);
    setData(_.sortBy(filteredData, sorter).reverse());
  };

  const handleSort = clickedColumn => {
    if (column !== clickedColumn) {
      setColumn(clickedColumn);
      if (clickedColumn === "title" || "acceptsOffers" || "timeStamp") {
        setData(_.sortBy(filteredData, [clickedColumn]));
      } else {
        handleSortNumeric(clickedColumn);
      }
      setDirection("ascending");
      return;
    }

    if (clickedColumn === "title") {
      setData(_.sortBy(filteredData.reverse()));
    } else {
      handleSortReverse();
    }

    direction === "ascending"
      ? setDirection("descending")
      : setDirection("ascending");
  };

  const removeFavorite = card => {
    props.removeFavorite(card, props.loggedUser);
    props.setNotification(`You removed ${card.title}!`, 5);
  };

  if (!props.cardsToShow) return null;

  return (
    <>
      <Search />
      <Segment inverted color="blue">
        <Header inverted color="grey" size="medium">
          My Favorites
        </Header>
      </Segment>
      <Segment>Count: {props.cardsToShow.length}</Segment>
      <Responsive maxWidth={767}>
        <strong>Click to Sort:</strong>
      </Responsive>
      <Table sortable celled fixed striped>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell
              textAlign="center"
              sorted={column === "title" ? direction : null}
              onClick={() => handleSort("title")}
            >
              Card Title
            </Table.HeaderCell>
            <Table.HeaderCell
              width={2}
              textAlign="center"
              sorted={column === "updatedBids" ? direction : null}
              onClick={() => handleSort("updatedBids")}
            >
              # Bids
            </Table.HeaderCell>
            <Table.HeaderCell
              textAlign="center"
              sorted={column === "updatedPrice" ? direction : null}
              onClick={() => handleSort("updatedPrice")}
            >
              Price
            </Table.HeaderCell>
            <Table.HeaderCell
              textAlign="center"
              sorted={column === "timeStamp" ? direction : null}
              onClick={() => handleSort("timeStamp")}
            >
              Time Left
            </Table.HeaderCell>
            <Table.HeaderCell
              textAlign="center"
              sorted={column === "status" ? direction : null}
              onClick={() => handleSort("status")}
            >
              Status
            </Table.HeaderCell>
            <Table.HeaderCell textAlign="center" width={2}>
              Remove
            </Table.HeaderCell>
          </Table.Row>
        </Table.Header>
        <Table.Body>
          {!filteredData
            ? "Sorry No Cards Found"
            : filteredData.map(card => (
                <>
                  <Responsive maxWidth={767}>
                        <div className="ui piled compact segment">
                          <div className="ui card">
                            <div className="blurring dimmable image">
                              <div className="ui inverted dimmer">
                                <div className="content">
                                  <div className="center">
                                    <div className="ui red button view">
                                      VIEW
                                    </div>
                                  </div>
                                </div>
                              </div>
                              <Image
                                src={card.image}
                                href={card.itemURL}
                                centered
                                style={{ padding: "5px" }}
                              />
                            </div>
                            <div className="content">
                              <div
                                id="rate"
                                className="ui star rating right floated"
                                data-rating="3"
                              ></div>
                              <div className="header">
                                <a href={card.itemURL}>{card.title}</a>
                              </div>
                              <div
                                className="meta"
                                style={{ padding: "5px 0 0 0" }}
                              >
                                <span className="date">
                                  <i className="clock icon"></i> Ends in{" "}
                                  {moment
                                    .duration(card.timeLeft, "minutes")
                                    .humanize()}
                                </span>
                                <div style={{ padding: "10px 0 0 0" }}>
                                  <span>
                                    <Button color="green">
                                      ${card.updatedPrice}
                                    </Button>
                                  </span>
                                  <span class="right floated date">
                                    {" "}
                                    <Button
                                      onClick={() => removeFavorite(card)}
                                      color="red"
                                      icon="remove circle"
                                    />
                                  </span>
                                </div>
                              </div>
                            </div>
                            <div className="extra content">
                              <div
                                className="ui right labeled button"
                                data-content="Bids"
                                data-variation="tiny"
                                tabindex="0"
                              >
                                <div className="ui blue icon tiny button">
                                  <i className="gavel large icon"></i>
                                </div>
                                <a
                                  href={card.itemURL}
                                  className="ui basic blue left pointing label"
                                >
                                  {card.updatedBids}
                                </a>
                              </div>
                              <div
                                className="ui left labeled right floated button"
                                data-content="Watch Count"
                                data-variation="tiny"
                                tabindex="0"
                              >
                                <a
                                  href={card.itemURL}
                                  className="ui basic blue right pointing label"
                                >
                                  {card.status}
                                </a>
                                <div className="ui blue icon tiny button">
                                  <i className="history large icon"></i>
                                </div>
                              </div>
                            </div>
                          </div>
                        </div>
                  </Responsive>
                  <Responsive
                    as={"tr"}
                    minWidth={768}
                    style={{ width: "100%" }}
                  >
                    <Popup
                      trigger={
                        <Table.Cell>
                          <a href={card.itemURL} target={"_blank"}>
                            {card.title}
                          </a>
                        </Table.Cell>
                      }
                      content={
                        <img
                          alt={card.title}
                          src={card.image}
                          height="250"
                        ></img>
                      }
                      style={style}
                      size="small"
                      position="left center"
                    ></Popup>
                    <Table.Cell textAlign="center">
                      {card.updatedBids}
                    </Table.Cell>
                    <Table.Cell textAlign="center">
                      ${card.updatedPrice}
                    </Table.Cell>
                    <Table.Cell textAlign="center">
                      {moment.duration(card.timeLeft, "minutes").humanize()}
                    </Table.Cell>
                    <Table.Cell textAlign="center">{card.status}</Table.Cell>
                    <Table.Cell textAlign="center">
                      <Button
                        onClick={() => removeFavorite(card)}
                        color="red"
                        icon="remove circle"
                      />
                    </Table.Cell>
                  </Responsive>
                </>
              ))}
        </Table.Body>
        <Table.Footer>
          <Table.Row>
            <Table.HeaderCell colSpan="6"></Table.HeaderCell>
          </Table.Row>
        </Table.Footer>
      </Table>
    </>
  );
};

const mapStateToProps = state => {
  return {
    baseball: state.baseball,
    favorites: state.favorites,
    favUpdates: state.favUpdates,
    loggedUser: state.loggedUser,
    page: state.page,
    entries: state.entries,
    query: state.query,
    pageOutput: state.pageOutput,
    search: state.search,
    cardsToShow: searchCards(state)
  };
};

const mapDispatchToProps = {
  searchChange,
  fetchData,
  removeFavorite,
  getFavorites,
  getUpdates,
  setNotification
};

export default connect(mapStateToProps, mapDispatchToProps)(Favorites);

I suspected the issue might be where i'm using the section as I dont have any table rows or cells set within the Table Body. I tried wrapping that whole section with a Table.Row and a Table.Cell, but still getting the same error. Any ideas?

Upvotes: 0

Views: 1491

Answers (1)

wbd
wbd

Reputation: 272

If !filteredData === false then the only child of <Table.Body> is text.

As the error says, the table body cannot have text as a child.

Wrap the text like this <Table.Row><Table.Cell>Sorry no cards shown</Table.Cell></Table.Row>

Upvotes: 2

Related Questions