Change the displayed data dynamically in react-table v7 - javascript

Not sure if there is a good way to change the displayed data dynamically. For instance I would like boolean values to be displayed in the table as icons and without slowing down the rendering process too much. I'm using the latest version of react-table v7. I have added code from the Table component Thanks
import React from "react";
import { useTable, useRowSelect, useSortBy, usePagination } from "react-table";
import { CSVLink, CSVDownload } from "react-csv";
import { navigate } from "#reach/router";
function handleColumnsClick(url, id) {
if (url && id) navigate(`${url}${id}`);
}
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
);
}
);
function Table({ columns, data, allowSelection, direct }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
selectedFlatRows,
// pagination
page, // Instead of using 'rows', we'll use page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { selectedRowIds, pageIndex, pageSize },
} = useTable(
{
columns,
data,
initialState: { pageIndex: 0 },
},
useSortBy,
usePagination,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
{allowSelection && (
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
)}
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
{allowSelection && (
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
)}
</div>
),
},
...columns,
]);
}
);
// Render the UI for your table
return (
<>
<table
className="table table-sm table-striped table table-hover"
{...getTableProps()}
>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th
className="checkbox"
{...column.getHeaderProps(column.getSortByToggleProps())}
>
{column.render("Header")}
{/* Add a sort direction indicator */}
<span>
{column.isSorted
? column.isSortedDesc
? " 🔽"
: " 🔼"
: ""}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td
onClick={(e) =>
handleColumnsClick(
direct[1],
cell.row.original[direct[0]]
)
}
{...cell.getCellProps()}
>
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<nav aria-label="Page navigation example">
<ul className="pagination justify-content-center">
<li className="page-item ">
<a
className="page-link"
tabIndex={-1}
aria-disabled="true"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
>
{"<< First Page"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
{"<"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => nextPage()}
disabled={!canNextPage}
>
{" >"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
>
{"Last Page >>"}
</a>
</li>
</ul>
<ul className="pagination justify-content-right">
<div className="pagination">
<span>
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<select
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</ul>
<p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
</nav>
<pre>
{/* Display selected rows for debugging */}
{/* <code>
{JSON.stringify(
{
selectedRowIds: selectedRowIds,
"selectedFlatRows[].original": selectedFlatRows.map(
(d) => d.original
),
},
null,
2
)}
</code> */}
</pre>
</>
);
}
function setColumns(props) {
const { data } = props;
if (data !== undefined && data.length !== 0) {
const columnsKeys = Object.keys(data[0]);
return columnsKeys.map((column) => {
return {
Header: column,
accessor: column,
};
});
}
}
function SelectTable(props) {
const columns = props.columns ? props.columns : setColumns(props);
const { data, allowSelection, direct } = props;
const addSelection = props.allowSelection ? props.allowSelection : false;
if (columns && data.length)
return (
<>
<Table
columns={columns}
data={data}
allowSelection={addSelection}
direct={direct}
/>
<button className="btn-outline btn-sm">
<CSVLink data={data}>Export to CSV </CSVLink>
</button>
</>
);
else return null;
}
export default SelectTable;

Related

Under row selection column, the select all checkbox in the header shows incorrect status while using react table v8

Below, I am sharing the code that I have written so far for row selection:
import {
ApolloError,
LazyQueryExecFunction,
OperationVariables,
} from '#apollo/client';
import { CategoryList, Category } from '../common/lib/generated-types';
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
} from '#tanstack/react-table';
import classNames from 'classnames';
import { HTMLProps, useEffect, useRef, useState } from 'react';
export interface CategoriesProps {
loading: boolean;
error?: ApolloError;
categories?: CategoryList;
fetchCategories: LazyQueryExecFunction<
{
categories: CategoryList;
},
OperationVariables
>;
storeId: string;
}
const defaultColumns: ColumnDef<Category>[] = [
{
id: 'select',
header: ({ table }) => (
<IndeterminateCheckbox
{...{
checked: table.getIsAllRowsSelected(),
indeterminate: table.getIsSomeRowsSelected(),
onChange: table.getToggleAllRowsSelectedHandler(),
}}
/>
),
cell: ({ row }) => (
<IndeterminateCheckbox
{...{
checked: row.getIsSelected(),
indeterminate: row.getIsSomeSelected(),
onChange: row.getToggleSelectedHandler(),
}}
/>
),
},
{
accessorKey: 'name',
cell: info => info.getValue(),
header: 'Category Name',
},
{
accessorKey: 'slug',
cell: info => info.getValue(),
header: 'Slug',
},
{
accessorKey: 'assignmentMethod',
cell: info => info.getValue(),
header: 'Assignment',
},
{
accessorKey: 'isPrivate',
cell: info => (info.getValue() ? 'Private' : 'Public'),
header: 'Visibility',
},
];
function IndeterminateCheckbox({
indeterminate,
className = '',
...rest
}: { indeterminate?: boolean } & HTMLProps<HTMLInputElement>) {
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
if (typeof indeterminate === 'boolean' && ref.current) {
ref.current.indeterminate = !rest.checked && indeterminate;
}
}, [ref, indeterminate, rest.checked]);
return (
<input
type="checkbox"
ref={ref}
className={className + ' cursor-pointer'}
{...rest}
/>
);
}
export function Categories(props: CategoriesProps) {
const { error, loading, categories, fetchCategories, storeId } = props;
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 10,
});
const [sort, setSort] = useState({ name: 'ASC' });
const [rowSelection, setRowSelection] = useState({});
const pageCount = Math.ceil(
(categories?.totalItems ?? 0) / pagination.pageSize,
);
const categoriesTable = useReactTable({
data: categories?.items || [],
columns: defaultColumns,
columnResizeMode: 'onChange',
pageCount,
onRowSelectionChange: setRowSelection,
getRowId: row => row.id.toString(),
getCoreRowModel: getCoreRowModel(),
state: {
pagination,
rowSelection,
},
onPaginationChange: setPagination,
manualPagination: true,
});
useEffect(() => {
fetchCategories({
variables: {
options: {
take: pagination.pageSize,
skip: pagination.pageIndex * pagination.pageSize,
filter: { storeId: { eq: storeId } },
sort,
},
},
});
}, [
fetchCategories,
pagination.pageIndex,
pagination.pageSize,
sort,
storeId,
]);
if (error) {
return (
<div>
<h1>Error!</h1>
</div>
);
}
return (
<div>
<div className="overflow-x-auto">
<table
{...{
style: {
width: categoriesTable.getCenterTotalSize(),
},
}}
>
<thead>
{categoriesTable.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th
{...{
key: header.id,
colSpan: header.colSpan,
style: {
textAlign: 'left',
},
}}
className="group relative"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
<div
{...{
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
className: classNames(
'absolute right-0 top-0 w-[5px] h-full bg-gray-200 cursor-col-resize user-select-none touch-action-none opacity-0 transition-opacity duration-200 group-hover:opacity-100',
{
'bg-blue-400 opacity-100':
header.column.getIsResizing(),
},
),
}}
/>
</th>
))}
</tr>
))}
</thead>
{loading ? (
<tbody>
<tr>
<td colSpan={4}>Loading...</td>
</tr>
</tbody>
) : (
<tbody>
{categoriesTable.getRowModel().rows.map(row => (
<tr key={row.id}>
{row.getVisibleCells().map(cell => (
<td
{...{
key: cell.id,
}}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
))}
</tr>
))}
</tbody>
)}
</table>
</div>
<div className="flex items-center gap-2">
<button
className="border rounded p-1"
onClick={() => categoriesTable.setPageIndex(0)}
disabled={!categoriesTable.getCanPreviousPage()}
>
{'<<'}
</button>
<button
className="border rounded p-1"
onClick={() => categoriesTable.previousPage()}
disabled={!categoriesTable.getCanPreviousPage()}
>
{'<'}
</button>
<button
className="border rounded p-1"
onClick={() => categoriesTable.nextPage()}
disabled={!categoriesTable.getCanNextPage()}
>
{'>'}
</button>
<button
className="border rounded p-1"
onClick={() =>
categoriesTable.setPageIndex(categoriesTable.getPageCount() - 1)
}
disabled={!categoriesTable.getCanNextPage()}
>
{'>>'}
</button>
<span className="flex items-center gap-1">
<div>Page</div>
<strong>
{categoriesTable.getState().pagination.pageIndex + 1} of{' '}
{categoriesTable.getPageCount()}
</strong>
</span>
<span className="flex items-center gap-1">
| Go to page:
<input
type="number"
value={categoriesTable.getState().pagination.pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
categoriesTable.setPageIndex(page);
}}
className="border p-1 rounded w-16"
/>
</span>
<select
value={categoriesTable.getState().pagination.pageSize}
onChange={e => {
categoriesTable.setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
<div>{Object.keys(rowSelection).length} Rows Selected</div>
</div>
);
}
export default Categories;
What does the code do?
It fetches the paginated data from the server related to some categories - (Cat1 dataset, Cat2 dataset, Cat3 dataset)
Issue?
Row selection works well with data that resides locally.
But, let's say the paginated data is being fetched from the server, the limit is set to 10 per page and you have selected 7 rows from the 1st page. Now, go to page 2 and select 3 rows from the second page once the data is fetched. The Select All checkbox is unticked on page two whereas it should show indeterminate.
I believe this happens because row selection is only aware of 10 items per page. Although it has a memory of selected rows, it's not aware of out of how many items those rows were selected.
One of the approaches could be to save retrieved data in a state and pass it to useReactTable. But, the apollo client already handles caching for me. So, I do not have to save anything to state as clicking on next/previous provides a similar experience. Are there any other approaches where I can continue using apollo client's caching feature and still get the correct status of the Select All Checkbox?
I have created a code sandbox to reproduce the issue:
https://codesandbox.io/s/pedantic-austin-uqt9ry?file=/src/table.tsx

Accessing "More Details" when clicked in a React Table

I am still learning React. I have an array of fake data in a json file that I am using to populate a table that I made with react-table. I have the table as a component. I have added a "Details" button in a column on the table, and when it's clicked, I want a second component to appear on the screen below the table that contains additional details that I could not reasonable fit onto the table. I assume I need to do some sort of handleClick function that's called with the button's onClick, and use that handleClick to setState some information that will let access it through the Details component. On the table component, I did a forEach to assign everything it's own ID number in efforts to use that to somehow call upon that specific item for more details, but I'm not really sure how to utilize that.
App.js
import './App.css';
import Details from './components/Details'
import Table from './components/Table';
import React, { Component } from 'react';
class App extends Component {
state = {
}
handleClick(){
this.setState({
})
}
render() {
return (
<>
<Table />
<Details />
</>
)}
}
export default App;
Table.js
import React, { useMemo } from "react";
import Data from "../sample.json"
import { useTable, useSortBy, usePagination } from 'react-table'
import { Columns } from './Columns'
import './Table.css'
function Table() {
const columns = useMemo(() => Columns, [])
const data = useMemo(() => Data, [])
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state,
prepareRow,
} = useTable(
{columns, data, initialState: {pageSize: 5}},
useSortBy,
usePagination)
const { pageIndex } = state
Data.forEach((item, i) => {
item.id = i + 1
})
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
)})}
</tr>
)})}
</tbody>
</table>
<div>
<center>
<span>
Page{' '}{pageIndex + 1} of {pageOptions.length}{' '}
</span>
<button
onClick={() => previousPage()}
disabled={!canPreviousPage}>
Previous
</button>
<button
onClick={() => nextPage()}
disabled={!canNextPage}>
Next
</button>
</center>
</div>
</>
)
}
export default Table;
Columns.js
export const Columns = [
{
Header: 'ID',
accessor: 'id'
},
{
Header: 'Payee Name',
accessor: 'Payee.Name'
},
{
Header: 'Payee Fax',
accessor: 'Payee.Fax'
},
{
Header: 'Payee Phone',
accessor: 'Payee.Phone'
},
{
Header: 'Link',
accessor: '',
Cell: props => <button onClick={() => {}}>Details</button>
},
]
Screenshot below is more or less what I want to see. Just a table, and the expanded details for the last item clicked on that table. Right now, I simply have some code mapping over all the data and giving them their own cards. But I just want one at a time, ideally. Current code mapping everything will be below.
enter image description here
import React from 'react';
import Data from '../sample.json'
function Details() {
return (
<>
{ Data.map(item => {
return(
<div className="item">
<div className="itemRow1">
<div>
<h4>{ item.Payee.Name }</h4>
<div>{ item.Payee.Phone }</div>
<div>{ item.Payee.Address.Address1 }</div>
<div>{ item.Payee.Address.Address2 }</div>
<div>{ item.Payee.Address.City }</div>
<div>{ item.Payee.Address.StateOrProvince }</div>
<div>{ item.Payee.Address.Country }</div>
<div>{ item.Payee.Address.PostalCode }</div>
<br/>
<div>{ item.Payee.Attention} </div>
<div>{ item.Payee.SubmissionDate }</div>
</div>
<div>
<h4>Payment Information</h4>
<div>{ item.Payment.PAN }</div>
<div>{ item.Payment.CVV }</div>
<div>{ item.Payment.Exp }</div>
</div>
<div>
<h4>Remittance</h4>
<div>{ item.Remittance.map(payor => {
return(
<div className="payor">
<div>{ payor.PayorName }</div>
<div>{ payor.PayorId }</div>
<div>{ payor.InvoiceNo }</div>
<div>{ payor.Amount }</div>
<br/>
</div>
)})}
</div>
</div>
</div>
<div>
{item.Remittance.map(payor => {
return (
<div className="description">
{payor.Description}
</div>
)})}
</div>
</div>
)})}
</>
);
}
export default Details;

React Table column order

I'm trying to use the column reorder for React table, but when I do I get a warning
Please be sure to add the {provided.placeholder} React Node as a child of your Droppable.
In the docs though it says this method has been deprecated. I'm not sure what I'm doing wrong or why this isn't working.
The example I used does everything the same way and it works.
https://codesandbox.io/s/gargroh-v7-drag-column-beautiful-react-dnd-encww?file=/src/App.js
const getItemStyle = ({ isDragging, isDropAnimating }, draggableStyle) => ({
...draggableStyle,
userSelect: "none",
...(!isDragging && { transform: "translate(0,0)" }),
...(isDropAnimating && { transitionDuration: "0.001s" })
});
export default function Table({ columns, data, header, subheader }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
flatColumns,
setColumnOrder,
} = useTable({
columns,
data,
},
useSortBy,
useRowSelect,
useColumnOrder,
)
const currentColOrder = React.useRef();
return (
<>
<Header>{header}</Header>
<SubHeader>{subheader}</SubHeader>
<StyledTable {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<DragDropContext
onDragStart={() => {
currentColOrder.current = flatColumns.map(o => o.id);
}}
onDragUpdate={(dragUpdateObj) => {
const colOrder = [...currentColOrder.current];
const sIndex = dragUpdateObj.source.index;
const dIndex =
dragUpdateObj.destination && dragUpdateObj.destination.index;
if (typeof sIndex === "number" && typeof dIndex === "number") {
colOrder.splice(sIndex, 1);
colOrder.splice(dIndex, 0, dragUpdateObj.draggableId);
setColumnOrder(colOrder);
}
}}
>
<Droppable droppableId="droppable" direction="horizontal">
{(droppableProvided) => (
<tr
{...headerGroup.getHeaderGroupProps()}
ref={droppableProvided.innerRef}
>
{headerGroup.headers.map((column, index) => (
<Draggable
key={column.id}
draggableId={column.id}
index={index}
isDragDisabled={!column.accessor}
>
{(provided, snapshot) => {
return (
<th
{...column.getHeaderProps(column.getSortByToggleProps())}
className="row header-group"
>
<StyledHeader
{...column.isSorted ? (
column.isSortedDesc ? (
<Image src={DropDownArrowUp} alt="fireSpot" />
) : (
<Image src={DropDownArrow} alt="fireSpot" />
)
) : (
''
)}
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
style={{
...getItemStyle(
snapshot,
provided.draggableProps.style
)
}}
>
{column.render("Header")}
</StyledHeader>
</th>
);
}}
</Draggable>
))}
</tr>
)}
</Droppable>
</DragDropContext>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<StyledTr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
);
})}
</StyledTr> || (
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<StyledTr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
);
})}
</StyledTr>
);
})}
</tbody>
)
);
})}
</tbody>
</StyledTable>
</>
);
}

embedded function in .map to access mapped information

I am using a .map function to display details to user.
I would like a button so that when it is clicked it expands for the user displaying more in depth information.
As it is mapped I cannot set a function as I would normally (between constructor and render)as it would not understand the mapped information e.g r.AssignedWorkStation
essentially I am asking is it possible to put a function within here (example below) and then have this be able to access the mapped properties
const renderTodos = currentTodos.map(r => {
Test(){
if(me){
//function to do what I want
}
}
return (
<>
<div className="jumbotron">
<button className="btn btn-primary" style={{ float: "right" }}>
View Details
</button>
<br />
<li>
<b>Workstation : </b>
{r.AssignedWorkStation}
</li>
<li>
<b>Date: </b>
{r.Date}
</li>
<li>
<b>Status: </b>
{r.CompleteToken}
</li>
<br />
</div>
</>
);
});
Whole class code
var results = [];
class AdminWorkstations extends React.Component {
constructor() {
super();
this.state = {
questions: [],
viewDetails: false,
currentPage: 1,
todosPerPage: 4
};
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)
});
};
// test() {
// alert(r.AssignedWorkStation);
// }
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);
});
}
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
}
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);
debugger;
const renderTodos = currentTodos.map(r => {
return (
<>
<div className="jumbotron">
<button className="btn btn-primary" style={{ float: "right" }}>
View Details
</button>
<br />
<li>
<b>Workstation : </b>
{r.AssignedWorkStation}
</li>
<li>
<b>Date: </b>
{r.Date}
</li>
<li>
<b>Status: </b>
{r.CompleteToken}
</li>
<br />
{/* <Questions results={r}></Questions> */}
</div>
</>
);
});
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>
);
});
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>
</>
);
}
}
}
You should save your todos inside your component state, not compute it inside render.
You shouldn't have a global variable called results either, store that inside your component state as well.
Here is a small example:
fetch(`/admin-completed-workstations`)
.then(recordset => recordset.json())
.then(results => {
this.setState({ questions: results.recordset });
console.log(this.state.questions);
// Here, inside getQuestionByUniqueDate you should store result using this.setState instead of having a global variable
// Then you can simply move the entire renderTodo function outside the render function of this component
this.state.questions &&
this.getQuestionByUniqueDate(this.state.questions);
});
LE: Here is a comprehensive article about fetching data in React.js apps:
https://www.robinwieruch.de/react-fetching-data (I recommend reading it)
LE2: You can assign both results and todos inside your componentDidMount
getQuestionByUniqueDate(questions) {
const currentResults = this.state.results ? [...this.state.results] : [];
for (var i = 0; i < questions.length; i++) {
if (
!currentResults.find(q => q.Date == questions[i].Date) ||
!currentResults.find(
q => q.AssignedWorkStation == questions[i].AssignedWorkStation
)
) {
currentResults.push(questions[i]);
}
}
return currentResults;
}
fetch(`/admin-completed-workstations`)
.then(recordset => recordset.json())
.then(res => {
const results = res.recordset &&
this.getQuestionByUniqueDate(res.recordset);
// Logic for displaying current todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo);
this.setState({
questions: res.recordset,
results,
currentTodos,
amountOfWorkstations: results.length
});
});

React 16 Ternary operator used with button, called with function gives error Maximum update depth exceeded

My goal is when I click on any list items, I want to show hidden button as confirm. What I tried is to show button on-click on function name selectItem as follows :
<Col xs={3}>
<ul>
<h2>Your orders </h2>
{selectedItems.map((item, i) => (
<li key={i}>
{item.name} {item.cost} {item.quantity}
<span onClick={() => this.deleteItem(i)}>cancel</span>
</li>
))}
</ul>
{this.selectItem()
? <Button type="button" style={{ display: 'block' }}>Confrim</Button>
: <Button type="button" style={{ display: 'none' }}>Confrim</Button>
}
</Col>
This gives the error as follows
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
The question is how I use the function call to display hidden button and hide when I remove all Items.Thanks.
import React from "react";
import {
Form,
FormGroup,
Row,
FormControl,
Col,
Button,
Label,
Modal,
ButtonToolbar,
Table
} from "react-bootstrap";
const MorningDrinks = [
{
id: "1",
name: "Tea",
cost: 15
},
{
id: "2",
name: "Coffee",
cost: 15
},
{
id: "3",
name: "Milk",
cost: 15
}
];
const ChoclateDrinks = [
{
id: "4",
name: "Smoothie",
cost: 15
},
{
id: "5",
name: "Hot Chocolate",
cost: 15
}
];
class MenuCard extends React.Component {
state = {
selectedItems: []
};
selectItem = item => {
const { counter, selectedItems } = this.state;
const newItem = {
...item,
quantity: 1
};
const el = selectedItems.filter(el => el.id === newItem.id);
if (selectedItems.length === 0) {
this.setState({
selectedItems: selectedItems.concat([newItem])
});
} else {
if (el.length) {
const newSelectedItems = selectedItems.map(item => {
if (item.id === newItem.id) {
item.quantity++;
}
return item;
});
this.setState({
selectedItems: newSelectedItems
});
} else {
this.setState({
selectedItems: selectedItems.concat([newItem])
});
}
}
};
deleteItem(i) {
this.setState({
selectedItems: this.state.selectedItems.filter((item, index) => {
return index !== i;
})
});
}
render() {
const { counter, selectedItems } = this.state;
return (
<div className="container">
<p>
Welcome {this.props.name}! Pick your any Break-fast menu you want
</p>
<Row>
<Col xs={3}>
<ul>
<h2>Morning Drinks </h2>
{MorningDrinks.map((item, i) => (
<li
style={{ cursor: "pointer" }}
key={i}
onClick={() => this.selectItem(item)}
>
{item.name} {item.cost}
</li>
))}
</ul>
<ul>
<h2>Chocolate Drinks </h2>
{ChoclateDrinks.map((item, i) => (
<li
style={{ cursor: "pointer" }}
key={i}
onClick={() => this.selectItem(item)}
>
{item.name} {item.cost}
</li>
))}
</ul>
</Col>
<Col xs={3}>
<ul>
<h2>Your orders </h2>
{selectedItems.map((item, i) => (
<li key={i}>
{item.name} {item.cost} {item.quantity}
<span onClick={() => this.deleteItem(i)}>cancel</span>
</li>
))}
</ul>
<Button type="button" style={{display: 'none'}}>Confrim</Button>
</Col>
<Col xs={3}>
<ul>
<h3>Total</h3>
{selectedItems.reduce(
(acc, item) => acc + item.cost * item.quantity,
0
)}
</ul>
</Col>
</Row>
</div>
);
}
}
export default MenuCard;
this.selectItem()
Sets state, which can't be done inside a render method. Try to render the button by only reading from state.
{this.state.selectedItems.length > 0 ? ... : ...}

Categories

Resources