document.location.pathname updating when routing from class component to functional component in react-router - javascript

These are the routes:
const routes = [
{ url: "", component: Home }, //class
{ url: "doctors", component: Doctors }, //class
{ url: "activedevices", component: ActiveDevices }, //functional
{ url: "nurses", component: Nurse }, //functional
{ url: "patients", component: PatientTable }, //functional
{ url: "floor-management", component: FloorManagement }, //class
{ url: "organizations", component: Organizations }, //functional
]
<Switch>
{routes.map((data, i) => (
<Route
key={i}
exact
path={`/${data.url}`}
component={data.component}
/>
))}
<Route path="*" component={()=> <Redirect to='/' />} />
</Switch>
I'm using window.location.pathname to determine the current path and highlight the corresponding menu item.
Dashboard
Patients
Floor Management
It is working fine when I try to navigate between class to class component or class to functional component. But when I try functional to functional component, the window.document.pathname does not update which results in respective menu item not being highlighted and last one remain highlighed. But the routing still works, I'm routed to the clicked routes and it also shows updated route in the address bar but not inside the script.
These are images showing updates routes in address bar but not in pathname:
Nurses
Active Devices
Here is the code for a functional component:
import React, { useState, useRef, useEffect } from "react";
import { Table, Pagination, Badge, Dropdown } from "react-bootstrap";
import { Link } from "react-router-dom";
import ActiveDevicesRow from "./ActiveDeviceRow";
import {baseURL, API, BEARER_TOKEN} from '../../../config'
const ActiveDevices = () => {
const [activeDevices, setActiveDevices] = useState(null)
let serial=1;
var myHeaders = new Headers();
myHeaders.append("Authorization", BEARER_TOKEN);
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
async function getActiveDevices(){
await fetch(baseURL+API.DEVICES, requestOptions)
.then(res => res.json())
.then(res => {
if(Array.isArray(res)) setActiveDevices(res)
})
.catch(err => console.log(err))
// console.log(activeDevices)
}
useEffect(()=>{
getActiveDevices()
}, [])
const [data, setData] = useState(
document.querySelectorAll("#ActiveDevices_basic_table tbody tr")
);
const sort = 5;
const activePag = useRef(0);
const [test, settest] = useState(0);
// Active data
const chageData = (frist, sec) => {
for (var i = 0; i < data.length; ++i) {
if (i >= frist && i < sec) {
data[i].classList.remove("d-none");
} else {
data[i].classList.add("d-none");
}
}
};
// use effect
useEffect(() => {
setData(document.querySelectorAll("#ActiveDevices_basic_table tbody tr"));
}, [test]);
// Active pagginarion
activePag.current === 0 && chageData(0, sort);
// paggination
let paggination = Array(Math.ceil(data.length / sort))
.fill()
.map((_, i) => i + 1);
// Active paggination & chage data
const onClick = (i) => {
activePag.current = i;
chageData(activePag.current * sort, (activePag.current + 1) * sort);
settest(i);
};
// console.log(document.querySelectorAll(".sorting_1 input")[0].checked);
return (
<div className="col-12">
<div className="card">
<div className="card-header">
<h4 className="card-title">Active Devices</h4>
</div>
<div className="card-body">
<Table responsive className="w-100">
<div
id="ActiveDevices_basic_table"
className="dataTables_wrapper"
>
<table
id="example5"
className="display dataTable no-footer w-100"
style={{ minWidth: 845 }}
role="grid"
aria-describedby="example5_info"
>
<thead>
<tr role="row">
<th
className="sorting"
tabIndex={0}
aria-controls="example5"
rowSpan={1}
colSpan={1}
aria-label="Patient ID: activate to sort column ascending"
style={{ width: 73 }}
>
S.R. No.
</th>
<th
className="sorting"
tabIndex={0}
aria-controls="example5"
rowSpan={1}
colSpan={1}
aria-label="Date Check in: activate to sort column ascending"
style={{ width: 100 }}
>
ID
</th>
<th
className="sorting"
tabIndex={0}
aria-controls="example5"
rowSpan={1}
colSpan={1}
aria-label="Patient Name: activate to sort column ascending"
style={{ width: 100 }}
>
Name
</th>
<th
className="sorting"
tabIndex={0}
aria-controls="example5"
rowSpan={1}
colSpan={1}
aria-label="Doctor Assgined: activate to sort column ascending"
style={{ width: 120 }}
>
Serial
</th>
<th
className="sorting"
tabIndex={0}
aria-controls="example5"
rowSpan={1}
colSpan={1}
aria-label="Disease: activate to sort column ascending"
style={{ width: 62 }}
>
DeviceType
</th>
<th
className="sorting"
tabIndex={0}
aria-controls="example5"
rowSpan={1}
colSpan={1}
aria-label="Action: activate to sort column ascending"
style={{ width: 47 }}
>
State
</th>
</tr>
</thead>
<tbody>
{activeDevices && activeDevices.length !== 0 ? activeDevices.map((device, index)=>
<ActiveDevicesRow
key={'device'+device.id}
srNo={serial++}
id={device.id}
serial={device.serial}
name={device.name}
deviceStatus={device.isActive}
deviceType={device.deviceType}
onClick=""
/>
)
: <tr><td colSpan='7' style={{textAlign:'center'}}>No Data Available</td></tr>
}
</tbody>
</table>
<div className="d-flex justify-content-between align-items-center mt-3">
<div className="dataTables_info">
Showing {activePag.current * sort + 1} to{" "}
{data.length > (activePag.current + 1) * sort
? (activePag.current + 1) * sort
: data.length}{" "}
of {data.length} entries
</div>
<div
className="dataTables_paginate paging_simple_numbers"
id="example5_paginate"
>
<Link
className="paginate_button previous disabled"
to="#"
onClick={() =>
activePag.current > 0 &&
onClick(activePag.current - 1)
}
>
Previous
</Link>
<span>
{paggination.map((number, i) => (
<Link
key={i}
to="#"
className={`paginate_button ${
activePag.current === i ? "current" : ""
} ${i > 0 ? "ml-1" : ""}`}
onClick={() => onClick(i)}
>
{number}
</Link>
))}
</span>
<Link
className="paginate_button next"
to="#"
onClick={() =>
activePag.current + 1 < paggination.length &&
onClick(activePag.current + 1)
}
>
Next
</Link>
</div>
</div>
</div>
</Table>
</div>
</div>
</div>
);
};
export default ActiveDevices;
And here is the code for a class component:
import React, { Component } from "react";
import { Dropdown } from "react-bootstrap";
import { Link } from "react-router-dom";
import DoctorsAccordion from "./DoctorsAccordion";
class Doctors extends Component {
componentDidMount(){
document.title = "Active Doctors"
}
render() {
return (
<React.Fragment>
<div className="form-head d-flex mb-3 mb-lg-5 align-items-start">
<Link onClick={this.onClick} className="btn btn-danger">
+ New Doctor
</Link>
<div className="input-group search-area ml-auto d-inline-flex">
<input type="text" className="form-control" placeholder="Search here" />
<div className="input-group-append">
<span className="input-group-text c-pointer">
<i className="flaticon-381-search-2"></i>
</span>
</div>
</div>
<Dropdown className="ml-3">
<Dropdown.Toggle variant="outline-primary" id="dropdown-basic">
<i className="flaticon-381-controls-3 "></i> Filter
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
<Dropdown.Item href="#">A To Z List</Dropdown.Item>
<Dropdown.Item href="#">Z To A List</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
<Dropdown className="ml-3">
<Dropdown.Toggle variant="outline-primary" id="dropdown-basic">
Newest
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
<Dropdown.Item href="#">Newest</Dropdown.Item>
<Dropdown.Item href="#">Old</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
<Link onClick={this.onClick} className="btn btn-outline-primary ml-3">
<i className="flaticon-381-menu-1 mr-0"></i>
</Link>
</div>
<DoctorsAccordion />
</React.Fragment>
);
}
}
export default Doctors;
Here is a screenshot showing functional compoenent path being updated in address bar but no getting logged on console:
Screenshot of functional comp path not logged

why can't you use `
props.location.pathnameinsted of
window.location.pathname
to determine the current path and refer this link Read the current full URL with React?

Related

How to let React select component so extend its parent

I'm using react-select for a drop down menu looking like this
component looks like this :
<table className="w-full text-sm text-center text-projectlightblack overflow-hidden font-poppins my-10 border-t border-t-projectblue border-l border-l-projectblue ">
<thead className="text-xl bg-projectbackgroundgray text-projectlightblack font-medium"></thead>
<tbody className="font-normal text-lg whitespace-pre-line text-start px-[10px] ">
{data?.rows?.map((row, index) => {
return (
<tr
className="py-[10px] border-b border-b-projectblue border-r border-r-projectblue border-t-0 border-l-0"
key={index}
>
{row?.map((cell, index) => {
let {
value,
tailwindClasses,
cellType,
dataType,
address,
options,
} = processCell(cell, cellData) || {};
if (cellType == "input" && dataType == "list") {
return (
<td className="w-[300px] h-auto" key={index}>
{isLoading || isFetching ? (
<InputLoadingIndicator />
) : (
<Select
options={options}
isLoading={isLoading || isFetching}
value={value}
onChange={(option) => {
setQueryKey(option);
mutate({
addressValue: address,
dateValue: option.value,
});
}}
styles={{
control: (baseStyles, state) => ({
...baseStyles,
borderColor: state.isFocused ? "white" : "white",
}),
}}
menuPlacement="auto"
/>
)}
</td>
);
}
basically when I extend the dropdown menu and it's bigger than parent component it doesn't work , giving a fixed height to the table tag would fix it but it's a reusable component and that's ruin it , Any other fixes?

How to show particular table row detail when it is clicked (Reusable Component) in react

I have created a reusable table component but am facing an issue showing detail for the particular row. what I was doing is if the row id is equal to a particular row id then I was trying to show the detail, but in my case for all rows details are visible.
Codesandbox : reusableTableComponent
what I tried:
const TableCustm = ({ TableHeader, dataVal, selectedRowDetail }) => {
const [selectedTableRow, setSelectedTableRow] = useState(null);
console.log("selectedRowDetail", selectedRowDetail);
console.log("selectedTableRow", selectedTableRow);
const data = dataVal.map((row) => {
const rowData = [];
const keys = Object.keys(row);
keys.forEach((key, index) => {
if (index !== 0) {
rowData.push({
key: TableHeader[index],
val: row[key]
});
}
});
return (
<>
<tr onClick={() => setSelectedTableRow(row)}>
{rowData.map((i) => (
<td className="font-lato text-[14px] text-p_black font-semibold py-0">
<div className="d-flex py-2">{i.val}</div>
</td>
))}
</tr>
// **********************detail table Row ********************
<tr>
<td colspan={TableHeader.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
// *******************end detail
</>
);
});
return (
<Table responsive borderless>
<thead>
<tr>
{TableHeader.map((item) => (
<th key={item.id} className="font-normal">
<div className="flex py-[15px]">{item.label}</div>
</th>
))}
</tr>
</thead>
<tbody className="border-0">{data}</tbody>
</Table>
);
};
I can see what you're trying to do. I'm by no means a react specialist, but nothing on Netflix was gripping me, so took up your challenge.
You're almost there, just a few things that are happening that are getting in your way.
I've got this working on codesandbox: https://codesandbox.io/embed/goofy-tereshkova-66p1ki?fontsize=14&hidenavigation=1&theme=dark
1) React is re-rendering the App Component when you click on the row
I'm not sure the best way to get around this, but every time you click the row (even with my step 2), it will re-generate the UUID. I got around this by just hard coding the IDs, as I assume you'll have a better way for generating IDs (or will need to figure out a way to stop the reloading.
But, for now, hardcode the id so you can follow step 2
const testData = [
{
id: 1,
2) Your use of useState between the Parent (App) and Child (TableCustm) components.
Not sure if this is intentional, but you're duplicating selectedTableRow state in both components. What I think you should do is hold the state in the parent (App) component, but pass both the state and the setState method to the child component inside of app.js like so:
<TableCustm
TableHeader={TableHeader}
dataVal={dataValue}
selectedRowDetail={selectedRow}
setRow={setSelectedRow}
/>
So now, inside the child component (TableCustom.js) you can set the state of the selected row like so:
<tr
onClick={(i) => {
setRow(row.id);
}}
>
And then, becaue you're also passing down (from the Parent to Child component) the current selected row selectedRowDetail, you can then conditionally render the row on the screen.
{row.id === selectedRowDetail &&
<tr>
<td colspan={TableHeader.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
}
Also, you might want to add a conditional step when setting the state of the selected row to null, so when you click again it disappears:
<tr
onClick={(i) => {
if (row.id === selectedRowDetail) {
setRow(null);
} else {
setRow(row.id);
}
}}
>
Hope that helps!
When you are working with React you have to understand when to use state and when to use props.
In your scenario you have two approaches:
When you want to show many details at same time, each row manage it owns state.
When you want to show one detail at a time, you must likely want to the parent Table component to manage your state.
It seems you want the approach 2) show one detail at a time, so you have to show it based on the selected row with selectedTableRow === row:
import React, { useState } from "react";
import { Table } from "react-bootstrap";
const TableCustm = ({ TableHeader, dataVal, selectedRowDetail }) => {
const [selectedTableRow, setSelectedTableRow] = useState(null);
console.log("selectedRowDetail", selectedRowDetail);
console.log("selectedTableRow", selectedTableRow);
const data = dataVal.map((row) => {
const rowData = [];
const keys = Object.keys(row);
keys.forEach((key, index) => {
if (index !== 0) {
rowData.push({
key: TableHeader[index],
val: row[key]
});
}
});
return (
<>
<tr
onClick={() =>
setSelectedTableRow(selectedTableRow === row ? null : row)
}
>
{rowData.map((i) => (
<td className="font-lato text-[14px] text-p_black font-semibold py-0">
<div className="d-flex py-2">{i.val}</div>
</td>
))}
</tr>
{selectedTableRow === row && (
<tr>
<td colspan={TableHeader.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
)}
</>
);
});
return (
<Table responsive borderless>
<thead>
<tr>
{TableHeader.map((item) => (
<th key={item.id} className="font-normal">
<div className="flex py-[15px]">{item.label}</div>
</th>
))}
</tr>
</thead>
<tbody className="border-0">{data} </tbody>
</Table>
);
};
export default TableCustm;
CodeSandbox: https://codesandbox.io/s/epic-mcnulty-g0hqhw?file=/src/TableCustm.js
PS: I believe your code need refactoring and I highly recommend you the Code With Mosh videos to start working with React: https://www.youtube.com/watch?v=Ke90Tje7VS0
Refactored code (not ideal yet, but better):
import React, { useState } from "react";
import { Table } from "react-bootstrap";
const TableRow = ({ data, onClickRow, showDetails }) => {
return (
<>
<tr onClick={onClickRow}>
{data.map((item, i) => (
<td
key={i}
className="font-lato text-[14px] text-p_black font-semibold py-0"
>
<div className="d-flex py-2">{item.val}</div>
</td>
))}
</tr>
{showDetails && (
<tr>
<td colSpan={data.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
)}
</>
);
};
const TableCustm2 = ({ TableHeader, dataVal }) => {
const [selectedTableRow, setSelectedTableRow] = useState(null);
return (
<Table responsive borderless>
<thead>
<tr>
{TableHeader.map((item) => (
<th key={item.id} className="font-normal">
<div className="flex py-[15px]">{item.label}</div>
</th>
))}
</tr>
</thead>
<tbody className="border-0">
{dataVal.map((row, index) => (
<TableRow
key={index}
data={TableHeader.map((key) => ({
key: key.label,
val: row[key.label]
}))}
onClickRow={() => {
setSelectedTableRow(index);
}}
showDetails={selectedTableRow === index}
/>
))}
</tbody>
</Table>
);
};
export default TableCustm2;

How to solve an iteration case

I'm using logic to check my checkboxes. The functionality allows you to check the parent checkbox and all the children are checked. You can also check or uncheck an individual checkbox. The problem is that I'm using pagination in my component, so if I check the first checkbox on page 1, the first checkbox on page 2 will also be checked. I believe this is because the markup occurs by index. How can I solve this problem?
import { useUsers } from '../../services/hooks/useUsers';
import {
Box,
Button,
Checkbox,
Flex,
Heading,
Icon,
Table,
Tbody,
Td,
Th,
Thead,
Tr,
Text,
useBreakpointValue,
Spinner,
Link,
HStack,
useDisclosure,
} from '#chakra-ui/react';
import { Header } from '../../components/Header';
import { NotificationModal } from '../../components/NotificationModal';
import { Sidebar } from '../../components/Sidebar';
import { RiAddLine } from 'react-icons/ri';
import { CgImport } from 'react-icons/cg';
import { TbEdit } from 'react-icons/tb';
import { FaWhatsapp } from 'react-icons/fa';
import { CgNotes } from 'react-icons/cg';
import { Pagination } from '../../components/Pagination';
import NextLink from 'next/link';
import { useState } from 'react';
import { queryClient } from '../../services/queryClient';
import { api } from '../../services/api';
export default function UserList() {
const [page, setPage] = useState(1);
const { data, isLoading, isFetching, error } = useUsers(page);
const [checkedItems, setCheckedItems] = useState([false]);
const allChecked = checkedItems.every(Boolean);
const isIndeterminate = checkedItems.some(Boolean) && !allChecked;
async function handlePrefetchUser(userId: string) {
await queryClient.prefetchQuery(
['user', userId],
async () => {
const response = await api.get(`users/${userId}`);
return response.data;
},
{
staleTime: 1000 * 60 * 10,
}
);
}
return (
<Box>
<Header />
<Flex my="6" maxWidth={1480} mx="auto" w="94%">
<Sidebar />
<Box flex="1" borderRadius={8} bg="gray.800" p="8" overflow="hidden">
<Flex mb="8" justify="space-between" align="center">
<Heading size="lg" fontWeight="normal">
Alunos
{!isLoading && isFetching && (
<Spinner size="sm" color="gray.500" ml="4" />
)}
</Heading>
<HStack spacing={4}>
<NextLink href="/users/create" passHref>
<Button as="a" size="sm" fontSize="sm" colorScheme="orange">
<Icon as={RiAddLine} fontSize="20" />
</Button>
</NextLink>
<NotificationModal />
<Button
as="a"
size="sm"
fontSize="sm"
colorScheme="blue"
cursor="pointer"
>
<Icon as={CgImport} fontSize="20" />
</Button>
</HStack>
</Flex>
{isLoading ? (
<Flex justify="center" align="center">
<Spinner />
</Flex>
) : error ? (
<Flex justify="center">
<Text>Falha ao obter dados dos usuários.</Text>
</Flex>
) : (
<>
<Table colorScheme="whiteAlpha" overflow="none" size="md">
<Thead>
<Tr>
<Th px={['4', '4', '6']} color="gray.300">
<Checkbox
colorScheme="orange"
isChecked={allChecked}
isIndeterminate={isIndeterminate}
onChange={(e) =>
setCheckedItems(
data.users.map(() => e.target.checked)
)
}
/>
</Th>
<Th w="100%">Alunos</Th>
<Th>Ações</Th>
{/* <Th w="8"></Th> */}
</Tr>
</Thead>
<Tbody>
{data.users.map((user, index) => (
<Tr key={user.id}>
<Td px={['4', '4', '6']}>
<Checkbox
key={user.id}
isChecked={checkedItems[index]}
colorScheme="orange"
onChange={(e) =>
setCheckedItems([
...checkedItems.slice(0, index),
e.target.checked,
...checkedItems.slice(index + 1),
])
}
/>
</Td>
<Td>
<Box>
<Link
color="orange.400"
onMouseEnter={() => handlePrefetchUser(user.id)}
>
<Text fontWeight="bold" fontSize={['sm', 'md']}>
{user.name}
</Text>
</Link>
</Box>
</Td>
<Td>
<Box cursor="pointer">
<HStack spacing={6}>
<Icon
as={TbEdit}
color="gray.400"
fontSize="22"
_hover={{
color: 'white',
}}
/>
<Icon
as={FaWhatsapp}
color="gray.400"
fontSize="22"
_hover={{
color: 'white',
}}
/>
<Icon
as={CgNotes}
color="gray.400"
fontSize="22"
_hover={{
color: 'white',
}}
/>
</HStack>
</Box>
</Td>
</Tr>
))}
</Tbody>
</Table>
<Pagination
totalCountOfRegisters={data.totalCount}
currentPage={page}
onPageChange={setPage}
/>
</>
)}
</Box>
</Flex>
</Box>
);
}
Add a key attribute with a value equal to the page number for the component which wraps your checkboxes.
So when the page number changes the checkboxes component will re-initialize.

Page numbers do not always appear at first navigation due to functions being in the render method

I am making a page which is paginated however I am at the moment rendering these in the render() method. I understand this is not ideal and I am almost sure this is why sometimes upon navigation to the screen the page numbers do not appear(they appear after a refresh of the page)
Essentially all I want is to take these out of the render method but I have allot going on in the render method at the moment So I am stuck on how to transition this.
I have included this code If you could advise how to take these out I will be very greatful.
Whole class
import React from "react";
import { Link } from "react-router-dom";
import { Modal, DropdownButton, Dropdown } from "react-bootstrap";
import ReactDOM from "react-dom";
var goToken = false;
var results = [];
class AdminWorkstations extends React.Component {
constructor() {
super();
this.state = {
questions: [],
viewDetails: false,
currentPage: 1,
todosPerPage: 5
};
this.getQuestionByUniqueDate = this.getQuestionByUniqueDate.bind(this);
// this.test = this.test.bind(this);
}
// sets the questions form sql into state for questions
handleClick = event => {
this.setState({
currentPage: Number(event.target.id)
});
};
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
}
componentDidMount() {
fetch(`/admin-completed-workstations`)
.then(recordset => recordset.json())
.then(results => {
this.setState({ questions: results.recordset });
console.log(this.state.questions);
this.state.questions &&
this.getQuestionByUniqueDate(this.state.questions);
});
}
getQuestionByUniqueDate(questions) {
for (var i = 0; i < questions.length; i++) {
if (
!results.find(q => q.Date == questions[i].Date) ||
!results.find(
q => q.AssignedWorkStation == questions[i].AssignedWorkStation
)
) {
results.push(questions[i]);
this.setState({ amountOfWorkstations: results.length });
}
}
return results;
}
render() {
const { currentPage, todosPerPage } = this.state;
// Logic for displaying current todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo);
const pageNumbers = [];
for (
let i = 1;
i <= Math.ceil(this.state.amountOfWorkstations / todosPerPage);
i++
) {
pageNumbers.push(i);
}
const renderPageNumbers = pageNumbers.map(number => {
return (
<button
className="btn btn-primary"
key={number}
id={number}
onClick={this.handleClick}
>
{number}
</button>
);
});
const renderTodos = currentTodos.map(r => {
return (
<>
<div className="jumbotron">
<Questions
workStation={r.AssignedWorkStation}
date={r.Date}
completeToken={r.CompleteToken}
>
{" "}
</Questions>
</div>
</>
);
});
let selectedWorkStation = window.localStorage.getItem("Workstation");
console.log(this.state.questions);
if (this.state.questions.length) {
return (
<div>
<h2 style={{ textAlign: "center" }}>
Completed Workstation Assessments
</h2>
<ul>
<button disabled className="btn btn-secondary">
Workstation Assessments
</button>
<Link to="./admin-center">
<button className="btn btn-secondary">Edit Questions</button>
</Link>
<Link to="./admin-center-view-users">
<button className="btn btn-secondary">View Users</button>
</Link>
<DropdownButton
style={{ float: "right" }}
id="dropdown-basic-button"
title="Completed"
>
<Dropdown.Item>
{" "}
<Link to="admin-view-workstation-assessments-declined">
In Progress
</Link>
</Dropdown.Item>
</DropdownButton>{" "}
</ul>
<ul>
{renderTodos}{" "}
<div
style={{ userSelect: "none", cursor: "pointer" }}
id="page-numbers"
>
{renderPageNumbers}
</div>
</ul>
</div>
);
} else if (!this.state.questions.length) {
return (
<>
{" "}
<div>
<h3 style={{ textAlign: "center" }}></h3>
<ul>
<br />
<br />{" "}
<div>
<h6>
{" "}
<tr>
Desk Location Selected :{" "}
<u style={{ color: "grey" }}>{selectedWorkStation}</u>
</tr>
</h6>
</div>
<div className="jumbotron">
<li style={{ textAlign: "center" }}>
<b>no completed Workstation Self-Assessments</b>{" "}
</li>
</div>
</ul>
</div>
</>
);
}
}
}
Just render method
render() {
const { currentPage, todosPerPage } = this.state;
// Logic for displaying current todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo);
const pageNumbers = [];
for (
let i = 1;
i <= Math.ceil(this.state.amountOfWorkstations / todosPerPage);
i++
) {
pageNumbers.push(i);
}
const renderPageNumbers = pageNumbers.map(number => {
return (
<button
className="btn btn-primary"
key={number}
id={number}
onClick={this.handleClick}
>
{number}
</button>
);
});
const renderTodos = currentTodos.map(r => {
return (
<>
<div className="jumbotron">
<Questions
workStation={r.AssignedWorkStation}
date={r.Date}
completeToken={r.CompleteToken}
>
{" "}
</Questions>
</div>
</>
);
});
let selectedWorkStation = window.localStorage.getItem("Workstation");
console.log(this.state.questions);
if (this.state.questions.length) {
return (
<div>
<h2 style={{ textAlign: "center" }}>
Completed Workstation Assessments
</h2>
<ul>
<button disabled className="btn btn-secondary">
Workstation Assessments
</button>
<Link to="./admin-center">
<button className="btn btn-secondary">Edit Questions</button>
</Link>
<Link to="./admin-center-view-users">
<button className="btn btn-secondary">View Users</button>
</Link>
<DropdownButton
style={{ float: "right" }}
id="dropdown-basic-button"
title="Completed"
>
<Dropdown.Item>
{" "}
<Link to="admin-view-workstation-assessments-declined">
In Progress
</Link>
</Dropdown.Item>
</DropdownButton>{" "}
</ul>
<ul>
{renderTodos}{" "}
<div
style={{ userSelect: "none", cursor: "pointer" }}
id="page-numbers"
>
{renderPageNumbers}
</div>
</ul>
</div>
);
} else if (!this.state.questions.length) {
return (
<>
{" "}
<div>
<h3 style={{ textAlign: "center" }}></h3>
<ul>
<br />
<br />{" "}
<div>
<h6>
{" "}
<tr>
Desk Location Selected :{" "}
<u style={{ color: "grey" }}>{selectedWorkStation}</u>
</tr>
</h6>
</div>
<div className="jumbotron">
<li style={{ textAlign: "center" }}>
<b>no completed Workstation Self-Assessments</b>{" "}
</li>
</div>
</ul>
</div>
</>
);
}
}
}

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?
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>

Categories