reactjs - React Table Pagination appears to be broken - javascript

I am new to using React JS. I have been using react-table to create a component that can filter, sort and paginate some sample data from a JSON file.
Here is the link to the tutorial I have been following:
https://www.freakyjolly.com/react-table-tutorial/#.YBfqqZP7SL4
Here is what I am seeing at the moment, the pagination appears to be broken, I am seeing all of the data appear (1000 rows). I am trying to have around 5-10 records showing at a time.
Here is the App.js code.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import FilterTableComponent from './components/filter.pagination.sorting';
function App() {
return (
<div className="App">
<h3>Filter Table using <code>react-table</code></h3>
<FilterTableComponent />
</div>
);
}
export default App;
Here is the filter.paginate.sorting.js code
import React from "react";
import { useTable, useSortBy, usePagination, useFilters, useGlobalFilter, useAsyncDebounce } from 'react-table';
import 'bootstrap/dist/css/bootstrap.min.css';
import JSONDATA from './MOCK_DATA.json';
// Define a default UI for filtering
function GlobalFilter({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) {
const count = preGlobalFilteredRows.length
const [value, setValue] = React.useState(globalFilter)
const onChange = useAsyncDebounce(value => {
setGlobalFilter(value || undefined)
}, 200)
return (
<span>
Search:{' '}
<input
className="form-control"
value={value || ""}
onChange={e => {
setValue(e.target.value);
onChange(e.target.value);
}}
placeholder={`${count} records...`}
/>
</span>
)
}
function DefaultColumnFilter({
column: { filterValue, preFilteredRows, setFilter },
}) {
const count = preFilteredRows.length
return (
<input
className="form-control"
value={filterValue || ''}
onChange={e => {
setFilter(e.target.value || undefined)
}}
placeholder={`Search ${count} records...`}
/>
)
}
function Table({ columns, data }) {
const defaultColumn = React.useMemo(
() => ({
// Default Filter UI
Filter: DefaultColumnFilter,
}),
[]
)
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
rows,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state,
state: { pageIndex, pageSize },
preGlobalFilteredRows,
setGlobalFilter,
} = useTable(
{
columns,
data,
defaultColumn,
initialState: { pageIndex: 0, pageSize: 10 }
},
useFilters,
useGlobalFilter,
useSortBy,
usePagination
)
return (
<div>
<ul className="pagination">
<li className="page-item" onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
<a className="page-link">First</a>
</li>
<li className="page-item" onClick={() => previousPage()} disabled={!canPreviousPage}>
<a className="page-link">{'<'}</a>
</li>
<li className="page-item" onClick={() => nextPage()} disabled={!canNextPage}>
<a className="page-link">{'>'}</a>
</li>
<li className="page-item" onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
<a className="page-link">Last</a>
</li>
<li>
<a className="page-link">
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</a>
</li>
<li>
<a className="page-link">
<input
className="form-control"
type="number"
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0
gotoPage(page)
}}
style={{ width: '100px', height: '20px' }}
/>
</a>
</li>{' '}
<select
className="form-control"
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value))
}}
style={{ width: '120px', height: '38px' }}
>
{[5, 10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</ul>
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
<pre>
<code>
{JSON.stringify(
{
pageIndex,
pageSize,
pageCount,
canNextPage,
canPreviousPage,
},
null,
2
)}
</code>
</pre>
<table className="table" {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
{/* Render the columns filter UI */}
<div>{column.canFilter ? column.render('Filter') : null}</div>
<span>
{column.isSorted
? column.isSortedDesc
? ' '
: ' '
: ''}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
<br />
<div>Showing the first 20 results of {rows.length} rows</div>
<div>
<pre>
<code>{JSON.stringify(state.filters, null, 2)}</code>
</pre>
</div>
</div>
)
}
function FilterTableComponent() {
const columns = React.useMemo(
() => [
{
Header: 'Name',
columns: [
{
Header: 'WIP_ID',
accessor: 'WIP_ID',
},
{
Header: 'PRODUCT_SERIAL_NUMBER',
accessor: 'PRODUCT_SERIAL_NUMBER'
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'DATE',
accessor: 'DATE'
},
{
Header: 'BUCKET_NAME',
accessor: 'BUCKET_NAME'
},
{
Header: 'CREATED_TS',
accessor: 'CREATED_TS'
},
{
Header: 'FILE_NAME',
accessor: 'FILE_NAME'
},
],
},
],
[]
)
const data = JSONDATA
return (
<Table columns={columns} data={data} />
)
}
export default FilterTableComponent;
Here is a sample of what the JSON file looks like. Named - MOCK_DATA.json
[{"WIP_ID":"56c97f3e-1c3f-4463-beb2-1af58ebe0db0","PRODUCT_SERIAL_NUMBER":"eab8304c-43e2-4f70-a23a-2db75bf2ce50","DATE":"29/06/2020","BUCKET_NAME":"Bytecard","CREATED_TS":"03/10/2020","FILE_NAME":"elit_proin.tiff"},
{"WIP_ID":"b358a03b-fee6-4957-9017-1de3d9846264","PRODUCT_SERIAL_NUMBER":"b974e89e-9bf9-4329-bc13-1afc3bdd52e0","DATE":"20/12/2020","BUCKET_NAME":"Vagram","CREATED_TS":"25/11/2020","FILE_NAME":"condimentum_id_luctus.mov"},
{"WIP_ID":"9fab6d70-72bc-40ae-a99f-5ae31dc47aec","PRODUCT_SERIAL_NUMBER":"8f9f70ce-b940-486b-a003-5c9db031e54e","DATE":"15/02/2020","BUCKET_NAME":"Domainer","CREATED_TS":"06/05/2020","FILE_NAME":"interdum_in_ante.tiff"},
{"WIP_ID":"5bb40cfb-99dc-413a-8b5f-0b6612e45d34","PRODUCT_SERIAL_NUMBER":"3bab0d2b-5464-4c5d-b1c8-0ba6b32e60fa","DATE":"14/03/2020","BUCKET_NAME":"Lotlux","CREATED_TS":"11/05/2020","FILE_NAME":"diam_nam_tristique.avi"},
{"WIP_ID":"95ae9754-e288-4ceb-b7cf-1b27892e7ace","PRODUCT_SERIAL_NUMBER":"1280dfb1-152d-44fd-aed7-4de8b8b573a6","DATE":"04/04/2020","BUCKET_NAME":"Gembucket","CREATED_TS":"14/10/2020","FILE_NAME":"vehicula.pdf"},

I think you need to change this in your tbody
{rows.map((row,i) =>
to
{page.map((row,i) =>

Related

ecommerce website filtering issue mern stack

home.jsx
const [category,setCategory]=useState("")
const dispatch = useDispatch();
const { loading, products, error } = useSelector((state) => state.products);
useEffect(() => {
dispatch(fetchProducts({category}));
}, [fetchProducts,category]);
<div className="cat_men_article">
<Link to="../products" onClick={()=>setCategory("menswear")}>
<figure className="cat_men"></figure>
<div className="cat_men_text">
<h2>Men's fashion</h2>
<span>SHOP NOW</span>
</div>
</Link>
</div>
productReducer.jsx
export const fetchProducts = createAsyncThunk("products", async (data) => {
let response;
let { category, price, ratings, currentPage } = data;
response = await axios.get(
`http://localhost:80/products?page=${currentPage}&price[gte]=${price[0]}&price[lte]=${price[1]}&ratings[gte]=${ratings}`
);
if (category) {
response = await axios.get(
`http://localhost:80/products?category=${category}&page=${currentPage}&price[gte]=${price[0]}&price[lte]=${price[1]}&ratings[gte]=${ratings}`
);
}
console.log(response.data);
// Inferred return type: Promise<MyData>
return await Object.values(response.data)[0];
});
products.jsx
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProducts({ category, price, ratings, currentPage }));
}, [fetchProducts, category, price, ratings, currentPage]);
const { loading, products, error, productCount, resultPerPage } = useSelector(
(state) => state.products
);
{category_list.map((cat, i) => (
<>
<span
style={{ cursor: "pointer" }}
key={cat}
onClick={() => setCategory(cat)}
>
{cat}
</span>
<br />
</>
))}
<Box sx={{ width: 300, marginLeft: "10px" }}>
<Slider
value={price}
onChange={handleChange}
valueLabelDisplay="auto"
min={0}
max={20000}
/>
</Box>
<Rating
name="simple-controlled"
value={ratings}
onChange={(event, newValue) => {
setRatings(newValue);
}}
/>
<br />
<span onClick={() => setPrice([0, 1000])} style={{ cursor: "pointer" }}>
Under 1000
</span>
<br />
<span
onClick={() => setPrice([1000, 2000])}
style={{ cursor: "pointer" }}
>
1000 - 2000
</span>
<br />
<span
onClick={() => setPrice([2000, 3000])}
style={{ cursor: "pointer" }}
>
2000 - 3000
</span>
<br />
<span
onClick={() => setPrice([3000, 20000])}
style={{ cursor: "pointer" }}
>
Over 3000
</span>
{products.map((prod) => (
<HomeProduct product={prod} />
))}
I am here actually getting 2 major problems. One of the first is that when I click on the menswear category at home.jsx then I suppose to render its products page and display products according to categories which is not technically possible for me and the second thing is filtering is not working properly since there are showing 3 products all the time even though I have put more products on the database.

How can I ensure the refresh of a sub component's props?

I have a problem with RTK queries : I have a first component that encapsulates a table. When a refresh occurs - for example when I delete some data - the parent component receives the data and passes it to the child through props but the Table is never updated. So the deleted results still appear on the table.
What is the proper "react/redux" way to generate a refresh of the table ?
Here's some code :
const Results = (props) => {
const router = useRouter()
const { data, error, isError, isLoading, isFetching } = useGetItemsQuery();
if(isLoading) {
return (
<>
<Spinner animation="border" size="sm" role="status" />{' '} Please wait while Loading...
</>
)
}
if(isError) {
return (
<>
<Alert key="warning" variant="warning" style={{marginLeft: "10px"}}>
Warning - There was an error with the request : {error.error}
</Alert>
</>
)
}
const sizePerPageList = [
{
text: '5',
value: 5,
},
];
if (data){
return(
<>
<Card>
<Card.Body>
Traffic results
<TableResults data={data} sizePerPageList={sizePerPageList} />
</Card.Body>
</Card>
</>
)
}
}
export default Results;
And for the second component with the table :
const TableResults = (props) => {
console.log('props - data ', props.data);
const data = React.useMemo(
() => props.data,
[]
)
const columns = React.useMemo(
() => [
{
Header: 'bla bla',
accessor: 'blabla',
}
],
[]
)
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} />
</>
)
}
)
const {
getTableProps,
getTableBodyProps,
headerGroups,
page, // Instead of using 'rows', we'll use page,
// which has only the rows for the active page
// The rest of these things are super handy, too ;)
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
prepareRow,
state: { pageIndex, pageSize, selectedRowIds },
visibleColumns,
preGlobalFilteredRows,
setGlobalFilter,
selectedFlatRows
} = useTable({ columns, data }, useGlobalFilter, useFilters, 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: ({ getToggleAllPageRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllPageRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
])
} )
return (
<>
<DeleteItemButton items={selectedFlatRows} />
<BTable {...getTableProps()} striped bordered hover size="sm">
<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, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td
{...cell.getCellProps()}
style={{
// padding: '10px',
// border: 'solid 1px gray',
// background: 'papayawhip',
}}
>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</BTable>
<div className="pagination">
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{'<<'}
</button>{' '}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{'<'}
</button>{' '}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{'>'}
</button>{' '}
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
{'>>'}
</button>{' '}
<span>
Page{' '}
<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>
</>
)
}
export default TableResults;
The DeleteItemButton will generate a simple RTK query that is functioning very well, and that triggers a refresh of the data from the main component :
deleteItems: builder.mutation({
query(data) {
// send array
let sanitized;
sanitized = keywords.filter(item => item);
const data = {
items: sanitized
}
//console.log('data: ', data);
return {
url: `items`,
method: 'DELETE',
body: data
}
},
invalidatesTags: ['Items']
}),
The app is running under nextJS. Any help appreciated !
Thanks
What is the proper "react/redux" way to generate a refresh of the table?
The correct way to refresh your data is by using the providesTags and invalidatesTags features of RTK query, which you are already doing.
The reason that your table is not refreshing is because you explicitly told it to ignore all changes to props.data and to use the initial value for all subsequent renders. This is obviously not what you intended, but it's what you're doing here:
const data = React.useMemo(
() => props.data,
[]
)
A useMemo with an empty dependency array will only execute once. Generally speaking, all variables which you use inside the useMemo should be included as dependencies. So the dependencies array should be [props.data].
Of course this particular useMemo does not do anything and can be safely deleted. It would make sense to have a useMemo if you were doing some reformatting of the props.data variable and wanted to only execute the reformatting when the value of props.data changes.
const data = React.useMemo(
() => someExpensiveComputation(props.data),
[props.data]
);

File Upload Form does not work when using react hooks over component

I am facing the difficulty that I want to rewrite the component to use react hooks, yet suddenly the DropZone component stopped working. I need to change the Component to hooks due to the JWT authentication I use.
I have this (former) implementation of the component:
import React from "react";
import Dropzone from "react-dropzone";
import UploadFileService from "../../../services/upload-files.service";
import './FileUploadForm.css'
import {withTranslation} from "react-i18next";
import Button from "#mui/material/Button";
import LinearProgressWithLabel from "./LinearProgressWithLabel";
import Typography from "#mui/material/Typography";
import {Alert} from "#mui/material";
import AuthContext from "../../../context/AuthContext";
class FileUploadForm extends React.Component {
constructor(props) {
super(props);
this.upload = this.upload.bind(this);
this.removeFiles = this.removeFiles.bind(this);
this.onDrop = this.onDrop.bind(this);
this.uploadFiles = this.uploadFiles.bind(this);
this.state = {
selectedFiles: undefined,
progressInfos: [],
message: [],
fileInfos: [],
dropZoneVisible: true,
url: props.url,
multipleFiles: props.multipleFiles
};
}
onDrop(files) {
if (files.length > 0) {
this.setState({selectedFiles: files});
}
}
uploadFiles() {
const selectedFiles = this.state.selectedFiles;
let _progressInfos = [];
for (let i = 0; i < selectedFiles.length; i++) {
_progressInfos.push({percentage: 0, fileName: selectedFiles[i].name});
}
this.setState(
{
progressInfos: _progressInfos,
message: [],
},
() => {
for (let i = 0; i < selectedFiles.length; i++) {
this.upload(i, selectedFiles[i]);
}
}
);
}
upload(idx, file) {
let _progressInfos = [...this.state.progressInfos];
UploadFileService.upload(file, this.state.url, (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
this.setState({
_progressInfos,
});
})
.then((response) => {
this.setState((prev) => {
let nextMessage = [
...prev.message,
"Uploaded the file successfully: " + file.name,
];
return {
message: nextMessage,
dropZoneVisible: false
};
});
return UploadFileService.getFiles(this.state.url);
})
.then((files) => {
this.setState({
fileInfos: files.data,
});
})
.catch(() => {
_progressInfos[idx].percentage = 0;
this.setState((prev) => {
let nextMessage = [
...prev.message,
"Could not upload the file: " + file.name,
];
return {
progressInfos: _progressInfos,
message: nextMessage,
};
});
});
}
render() {
const {t} = this.props;
const {selectedFiles, progressInfos, message, fileInfos} = this.state;
return (
<div style={{marginTop: '1em'}}>
{progressInfos &&
progressInfos.map((progressInfo, index) => (
<div className="mb-2" key={index} style={{
textAlign: "center",
fontWeight: "bold"
}}>
<Typography component="span" variant="string" align="center">
{progressInfo.fileName}
</Typography>
<LinearProgressWithLabel variant="determinate" value={progressInfo.percentage}/>
</div>
))}
<Dropzone onDrop={this.onDrop} multiple={this.state.multipleFiles}>
{({getRootProps, getInputProps}) => (
<section>
<div {...getRootProps({className: "dropzone"})}>
<input {...getInputProps()} />
{selectedFiles &&
Array.isArray(selectedFiles) &&
selectedFiles.length ? (
<div className="selected-file">
{selectedFiles.length > 3
? `${selectedFiles.length} files`
: selectedFiles.map((file) => file.name).join(", ")}
</div>
) : (
t("label", "Drag and drop file here, or click to select file", {ns: "fileUploadForm"})
)}
</div>
<aside className="selected-file-wrapper"
style={{
display: "flex",
justifyContent: "space-evenly"
}}
>
<Button
className="btn btn-success"
disabled={!selectedFiles}
variant="contained"
onClick={this.uploadFiles}
>
{t("button", "Submit", {ns: "fileUploadForm"})}
</Button>
<Button
className="btn"
color="error"
disabled={!selectedFiles}
variant="contained"
onClick={this.removeFiles}
>
{/*{t("button", "Submit", {ns: "fileUploadForm"})}*/}
Beheben
</Button>
</aside>
</section>
)}
</Dropzone>
{message.length > 0 && (
<div style={{
marginTop: "1em"
}}>
{message.map((item, i) => {
return (
<Alert severity={
item.includes("success") ? "success" : "warning"}>
{item}
</Alert>);
})}
</div>
)}
{fileInfos.length > 0 && (
<div className="card">
<div className="card-header">List of Files</div>
<ul className="list-group list-group-flush">
{fileInfos && fileInfos.map((file, index) => (
<li className="list-group-item" key={index}>
<a href={file.url}>{file.name}</a>
</li>
))}
</ul>
</div>
)}
</div>
);
}
}
export default withTranslation()(FileUploadForm);
This component works perfectly, meaning that the selectedFiles get propagated so the users can see what they submit. However, when changed to the functional implementation using hooks:
import React, {useCallback, useEffect, useRef, useState} from "react";
import Dropzone from "react-dropzone";
import './FileUploadForm.css'
import {useTranslation, withTranslation} from "react-i18next";
import Button from "#mui/material/Button";
import LinearProgressWithLabel from "./LinearProgressWithLabel";
import Typography from "#mui/material/Typography";
import {Alert} from "#mui/material";
import useAxios from "../../../utils/useAxios";
function FileUploadForm(url, multipleFiles, handleUploadReload) {
const api = useAxios();
const {t, i18n} = useTranslation()
const {selectedFiles, setSelectedFiles} = useState(undefined);
const {progressInfos, setProgressInfos} = useState({val: []});
const {message, setMessage} = useState([]);
const {targetUrl, setTargetUrl} = useState(url);
const {multipleFilesEnabled, setMultipleFilesEnabled} = useState(multipleFiles);
const {formUploaded, setFormUploaded} = useState(false);
const progressInfosRef = useRef(null)
const onDrop = (files) => {
if (files && files.length > 0) {
setSelectedFiles(files);
setProgressInfos({ val: [] });
}
}
const resetFormState = () => {
setFormUploaded(false);
setSelectedFiles(undefined);
setProgressInfos([]);
setMessage([]);
}
const removeFiles = () => {
setSelectedFiles(undefined);
setProgressInfos([]);
setMessage([]);
}
const uploadFiles = () => {
const files = Array.from(selectedFiles);
let _progressInfos = files.map(file => ({percentage: 0, fileName: file.name}));
progressInfosRef.current = {
val: _progressInfos,
}
files.map((file, i) => upload(i, file));
setMessage([]);
};
const upload = (idx, file) => {
let _progressInfos = [...progressInfosRef.current.val];
let formData = new FormData();
formData.append("file", file)
const onUploadProgress = (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
setProgressInfos({val: _progressInfos});
}
return api.post(targetUrl, formData, {
headers: {
"Content-Type": "multipart/form-data",
}, onUploadProgress
}).then((response) => {
handleUploadReload(response.data["files"]);
setMessage((prev) => ([
...prev.message,
"Uploaded the file successfully: " + file.name,
]
));
setFormUploaded(true);
})
.catch(() => {
_progressInfos[idx].percentage = 0;
setProgressInfos({val: _progressInfos});
setMessage((prev) => ([
...prev.message,
"Could not upload the file: " + file.name,
]));
});
}
return (
<div style={{marginTop: '1em'}}>
{progressInfos &&
progressInfos.val.length > 0 &&
progressInfos.val.map((progressInfo, index) => (
<div className="mb-2" key={index} style={{
textAlign: "center",
fontWeight: "bold"
}}>
<Typography component="span" variant="string" align="center">
{progressInfo.fileName}
</Typography>
<LinearProgressWithLabel variant="determinate" value={progressInfo.percentage}/>
</div>
))}
<Dropzone onDrop={onDrop} multiple={multipleFilesEnabled}>
{({getRootProps, getInputProps}) => (
<section>
<div {...getRootProps({className: "dropzone"})}>
<input {...getInputProps()} />
{selectedFiles &&
Array.isArray(selectedFiles) &&
selectedFiles.length ? (
<div className="selected-file">
{selectedFiles.length > 3
? `${selectedFiles.length} files`
: selectedFiles.map((file) => file.name).join(", ")}
</div>
) : (
t("label", "Drag and drop file here, or click to select file", {ns: "fileUploadForm"})
)}
</div>
<aside className="selected-file-wrapper"
style={{
display: "flex",
justifyContent: "space-evenly"
}}
>
{!formUploaded ? [
<Button
className="btn btn-success"
disabled={!selectedFiles}
variant="contained"
onClick={uploadFiles}
>
{t("button", "Submit", {ns: "fileUploadForm"})}
</Button>,
<Button
className="btn"
color="error"
disabled={!selectedFiles}
variant="contained"
onClick={removeFiles}
>
{/* TODO: {t("button", "Submit", {ns: "fileUploadForm"})}*/}
Beheben
</Button>] : (
<Button
className="btn"
color="error"
variant="contained"
onClick={resetFormState}
>
{/*TODO: {t("button", "Submit", {ns: "fileUploadForm"})}*/}
Neu Hochladen
</Button>
)}
</aside>
</section>
)}
</Dropzone>
{message && message.length > 0 && (
<div style={{
marginTop: "1em"
}}>
{message.map((item, i) => {
return (
<Alert severity={
item.includes("success") ? "success" : "warning"}>
{item}
</Alert>);
})}
</div>
)}
</div>
);
}
export default withTranslation()(FileUploadForm);
where the utilization of useAxios is essential. Nevertheless, users cannot see what they uploaded (preload view before push request). Any clue what could be wrong?
TLDR
DropZone shows uploaded files in the browser (before post request) when FileUploadForm is class and does not in case of a function.

how to make InfiniteScroll implementation updated because it update only one time in React table?

import React, { useEffect, useMemo, useState } from 'react' ;
import { useTable , useSortBy } from 'react-table';
import MOCK_DATA from '../MOCK_DATA.json';
import { Columns } from './columns';
import {Checkbox} from './Checkbox';
import InfiniteScroll from 'react-infinite-scroll-component';
export default function DefaultTable() {
const columns = useMemo(() => Columns , [])
const data = useMemo(() => MOCK_DATA , [])
const [items , setItems] = useState(data);
const [hasMore , setHasMore] = useState(true)
const [offset, setOffset] = useState(10);
const fetchMoreData = () => {
// a fake async api call like which sends
// 20 more records in .5 secs
if (items.length >= items.length +1) {
setHasMore( false );
return;
}
setTimeout(() => {
setOffset( offset + 10);
}, 100);
};
const {getTableProps ,
getTableBodyProps ,
headerGroups ,
rows ,
prepareRow,
allColumns,
getToggleHideAllColumnsProps
} = useTable ({
columns ,
data
},
useSortBy
)
return (
<div>
<div>
<Checkbox {...getToggleHideAllColumnsProps()} /> Toggle All
{
allColumns.map(column =>(
<span key={column.id}>
<label>
<input type='checkbox' {...column.getToggleHiddenProps()} />
{column.Header}
</label>
</span>
))
}
</div>
<div
id="scrollableDiv"
style={{
height: 320,
overflow: 'auto',
display: 'flex',
}}
>
<InfiniteScroll
dataLength={rows.length} //This is important field to render the next data
next={fetchMoreData}
hasMore={hasMore}
style={{ display: 'flex', flexDirection: 'column' }}
loader={<h4>Loading more items...</h4>}
scrollableTarget="scrollableDiv"
endMessage={
<p style={{ textAlign: 'center' }}>
<b>Yay! You have seen it all</b>
</p>
}
>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render('Header')}
<span>
{column.isSorted
? column.isSortedDesc
? ' 🔽'
: ' 🔼'
: ''}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.slice(0, offset).map((row) => {
// {rows.map((row) => {
prepareRow(row)
return(
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
</InfiniteScroll>
</div>
</div>
)
}

React error Objects are not valid as a React child (found: object with keys {$$typeof, type, compare)

I got a legacy project of Meteor and React and I try to make it work. But I got an error at one point. Can anyone help me to resolve following error?
Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare}). If you meant to render a collection of children, use an array instead.
in Unknown
in main (created by Basic)
in Basic (created by Context.Consumer)
in Content (created by PageLayout)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Layout (created by PageLayout)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Layout (created by PageLayout)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Layout (created by PageLayout)
in LocaleProvider (created by Context.Consumer)
in LocaleReceiver (created by ConfigProvider)
in ConfigProvider (created by PageLayout)
in PageLayout (created by ForwardRef)
in ForwardRef
in ForwardRef
at throwOnInvalidObjectType (modules.js:64084)
at reconcileChildFibers (modules.js:64984)
at reconcileChildren (modules.js:67433)
at mountIndeterminateComponent (modules.js:68213)
at beginWork (modules.js:69267)
at HTMLUnknownElement.callCallback (modules.js:50859)
at Object.invokeGuardedCallbackDev (modules.js:50908)
at invokeGuardedCallback (modules.js:50963)
at beginWork$1 (modules.js:73874)
at performUnitOfWork (modules.js:72828)
Within the secure.js the PostCategories are mounted like this
ContentRouter.route('/content-creator', {
name: 'Content Creator',
icon: 'solution',
permission: 'main-navigation.view.module.posts',
className: 'posts',
visible: true,
action: () => {
mount(PageLayout, { content: <PostCategories /> });
},
});
The PageLayout looks like this :
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import React, { Component } from 'react';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Konami from 'react-konami-code';
import deDE from 'antd/lib/locale-provider/de_DE';
import moment from 'moment';
import 'moment/locale/de';
import {
ConfigProvider, Layout, Menu, Icon, Steps, Badge, Button, message,
} from 'antd';
import { Workspaces } from '../../../api/workspaces/collection';
import DoneStepper from './components/doneStepper';
import { DASHBOARD_VERSION } from '../../../helpers/Constants';
const {
Header, Content, Sider, Footer,
} = Layout;
moment.locale('de');
class PageLayout extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
logout = () => {
Meteor.logout(() => {
FlowRouter.go('/anmelden');
});
};
renderMenuItems = () => {
const groupName = FlowRouter.current().route.group.name;
const items = FlowRouter._routes.map((route) => {
const isSecure = route && route.group && route.group.parent && route.group.parent && route.group.parent.name === 'secure';
const isVisible = route && route.options && route.options.visible && route.options.visible === true;
const isActive = groupName === route.group.name;
let active = null;
if (isActive) {
active = 'ant-menu-item-selected';
}
return (
isVisible
&& isSecure && (
<Menu.Item key={route.path} style={route.options.style} className={[route.options.className, active]}>
<a href={route.path}>
<Icon type={route.options.icon} />
<span className="nav-text">{route.name}</span>
</a>
</Menu.Item>
)
);
});
return items;
};
easterEgg = () => {
message.success('Development Mode enabled');
};
handleResetWorkspace = () => {
const { user } = this.props;
Meteor.call('workspace.reset', user.profile.workspace, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
}
});
};
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
console.log('ERROR IS:');
console.log(this.state.error);
return <h1>Something went wrong.</h1>;
}
const { ready, user, workspace, content } = this.props;
if (ready) {
if (workspace && workspace.appName && workspace.appName.name) {
document.title = `App - ${workspace.appName.name}`;
} else {
document.title = 'App';
}
}
return (
<ConfigProvider locale={deDE}>
<Layout className="manager-dashboard">
<Header className="header">
<div className="logo__area">
<a href="/">
<img className="logo" src="/logo.svg" alt="cms" />
</a>
</div>
<DoneStepper />
<Konami action={this.easterEgg}>
<Button type="primary" onClick={() => this.handleResetWorkspace()}>
<Icon type="delete" size="small" />
{'reset workspace'}
</Button>
</Konami>
<div className="member">
<a onClick={this.logout}>
{'Abmelden '}
<Icon type="logout" />
</a>
</div>
</Header>
<Layout>
<Sider width={200} style={{ background: '#fff' }}>
<div className="fixed-sidebar">
<Menu
defaultSelectedKeys={[FlowRouter.current().route.path]}
selectedKeys={[FlowRouter.current().route.path]}
>
{this.renderMenuItems()}
</Menu>
</div>
</Sider>
<Layout className="dashboard__content">
<Content
style={{
background: '#fff',
padding: 20,
minHeight: '100vh',
}}
>
{content}
</Content>
<Footer>{`App - Version ${DASHBOARD_VERSION}`}</Footer>
</Layout>
</Layout>
</Layout>
</ConfigProvider>
);
}
}
export default withTracker(() => {
const user = Meteor.user();
const handleWorkspace = Meteor.subscribe('workspaces.one', {
query: { _id: user.profile.workspace },
fields: { appName: 1 },
});
const ready = handleWorkspace.ready();
let workspace = null;
if (ready) {
workspace = Workspaces.findOne({ _id: user.profile.workspace }, { fields: { appName: 1 } });
}
return {
ready,
user,
workspace,
};
})(PageLayout);
And the PostCategories look like this:
/* eslint-disable jsx-a11y/click-events-have-key-events */
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { PostCategories } from '/imports/api/posts/collection';
import React, { Component } from 'react';
import Role from '/imports/helpers/Role';
import CreatePostCategory from '/imports/ui/secure/partials/forms/createPostCategory.js';
import {
Card, Button, message, Row, Col, Icon, Switch, Popover,
} from 'antd';
import Text from '../../components/Text.js';
import Preview from '../../components/preview/index.js';
import CustomSpinner from '../../components/custom-spinner';
const DragIndicator = () => (
<svg viewBox="0 0 14 5" height="15" width="15" xmlns="http://www.w3.org/2000/svg">
<path d="m0 0h14v.83823529h-14z" />
<path d="m0 2h14v.83823529h-14z" />
<path d="m0 4h14v.83823529h-14z" />
</svg>
);
const getItemStyle = (isDragging, draggableStyle) => ({
userSelect: 'none',
...draggableStyle,
});
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
class Categories extends Component {
constructor(props) {
super(props);
this.state = {
categories: props.categories,
createCategory: false,
justReorder: false,
deletePopoverVisible: {},
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.ready && !prevState.justReorder) {
prevState.categories = nextProps.categories;
prevState.justReorder = false;
} else {
prevState.justReorder = false;
}
return prevState;
}
handleCategoryState = (id, checked) => {
Meteor.call('post.category.state', id, checked, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
}
});
};
handleDeletePopoverVisibleChange = (change, id) => {
this.state.deletePopoverVisible[id] = change;
this.setState(this.state);
};
handleDelete = (e, id) => {
e.preventDefault();
Meteor.call('post.category.delete', id, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
}
});
};
handleCreateCategory = (e) => {
e.preventDefault();
const form = this.createCategoryForm;
form.validateFieldsAndScroll((err, values) => {
if (err) {
return;
}
values.workspace = this.props.user.profile.workspace;
values.index = this.props.categories.length + 1;
Meteor.call('post.category.insert', values, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
form.resetFields();
this.setState({ createCategory: false, categoryId: null });
}
});
});
};
onDragEnd = (data) => {
console.log(data);
const { source, destination, draggableId } = data;
if (!destination) {
return;
}
if (source.droppableId === destination.droppableId) {
this.state.categories = reorder(this.state.categories, data.source.index, data.destination.index);
this.state.categories.forEach((category, idx) => (category.index = idx));
this.state.justReorder = true;
this.setState(this.state, () => {
Meteor.call('post.category.reorder', { categories: this.state.categories }, (error, result) => {
if (error) {
console.log(error);
message.error('error.');
}
});
});
}
};
render() {
const { ready, categories } = this.props;
const { categories: stateCategories, deletePopoverVisible, createCategory } = this.state;
let content = <CustomSpinner />;
if (ready) {
content = (
<Col>
<Row gutter={40}>
<Col xs={24} lg={16} xxl={18}>
{!Array.isArray(categories) ? (
<Row gutter={40}>
<Col
xs={{ span: 24 }}
lg={{ span: 18, offset: 3 }}
xl={{ span: 12, offset: 6 }}
style={{ textAlign: 'center' }}
>
<img src="/emptystates/contentcreator.svg" alt="empty dashboard" />
</Col>
<Col
xs={{ span: 24 }}
lg={{ span: 18, offset: 3 }}
xl={{ span: 16, offset: 4 }}
style={{ textAlign: 'center', marginTop: '40px' }}
>
<h2>Head</h2>
<p>
Text
</p>
<Button
className="button--center"
size="large"
type="primary"
onClick={() => this.setState({ createCategory: true })}
>
Los geht's
<Icon type="right" />
</Button>
</Col>
</Row>
) : (
<div>
<div className="page-title">
<h1>Content Creator</h1>
<p>
Text
</p>
</div>
<Row gutter={40}>
<Col xs={24}>
<div className="shadow-wrapper">
<DragDropContext
onDragEnd={this.onDragEnd}
>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{stateCategories.map((category, index) => (
<Draggable key={category._id} draggableId={category._id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
>
<Card className="card--list-view">
<div>
<div className="card__title">
<span {...provided.dragHandleProps}>
<Icon
component={DragIndicator}
style={{
paddingRight: '20px',
paddingTop: '6px',
}}
/>
</span>
<a href={`/content-creator/kategorie/${category._id}`}>
{category.title}
</a>
</div>
<div className="card__edit">
<span>
<a
href={`/content-creator/neuen-artikel-anlegen?id=${category._id}`}
onClick={() => FlowRouter.go(
'/content-creator/neuen-artikel-anlegen',
{},
{ id: category._id },
)
}
>
<Icon type="folder-add" />
{' Artikel hinzufügen'}
</a>
</span>
<Text
label="Umbenennen der Kategorie"
required
message="Bitte tragen Sie einen Kategorietitel ein."
initialValue={category.title}
render={(
<span>
<Icon
style={{
marginRight: 5,
}}
type="edit"
/>
Umbenennen
</span>
)}
onValid={(result) => {
Meteor.call(
'post.category.edit',
category._id,
{ title: result },
(e) => {
if (e) {
console.log(e);
message.error('Bei dieser Aktion ist ein Fehler aufgetreten');
} else {
message.success('Kategorie Titel erfolgreich erstellt');
}
},
);
}}
/>
<Popover
content={(
<div className="manager-dashboard">
<div
className="page__actions"
style={{ marginBottom: 0, marginTop: 0 }}
>
<Button
type="danger"
onClick={() => this.handleDeletePopoverVisibleChange(false, category._id)
}
>
<Icon type="close" />
{' Abbrechen'}
</Button>
<Button
type="primary"
style={{ marginLeft: '10px' }}
onClick={event => this.handleDelete(event, category._id)}
>
{'Löschen '}
<Icon type="delete" />
</Button>
</div>
</div>
)}
title="Diese Kategorie wirklich löschen?"
trigger="click"
visible={deletePopoverVisible[category._id]}
onVisibleChange={change => this.handleDeletePopoverVisibleChange(change, category._id)
}
>
<Icon type="delete" />
{' Löschen'}
</Popover>
<Switch
style={{ float: 'right' }}
defaultChecked={category.state}
onChange={checked => this.handleCategoryState(category._id, checked)}
checkedChildren="Öffentlich"
unCheckedChildren="Entwurf"
/>
</div>
</div>
</Card>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
{Role.hasPermission('product.category.create') && (
<Card
className="card--list-view add-new-card"
onClick={() => this.setState({ createCategory: true })}
>
<div className="card__title">
<a>+ Neue Kategorie hinzufügen</a>
</div>
</Card>
)}
</div>
</Col>
</Row>
</div>
)}
{createCategory && (
<CreatePostCategory
ref={(form) => {
this.createCategoryForm = form;
}}
visible={createCategory}
onCancel={() => this.setState({ createCategory: false })}
onCreate={this.handleCreateCategory}
/>
)}
</Col>
<Col xs={24} sm={12} lg={8} xxl={6}>
<Preview type="categories" />
</Col>
</Row>
</Col>
);
}
return content;
}
}
export default withTracker(() => {
const user = Meteor.user();
const handleCategories = Meteor.subscribe('posts.categories.all', {
query: { workspace: user.profile.workspace },
sort: { index: 1, title: 1 },
fields: {
title: 1, state: 1, index: 1, workspace: 1,
},
});
const ready = handleCategories.ready();
let categories = null;
if (ready) {
categories = PostCategories.find(
{ workspace: user.profile.workspace },
{
sort: { index: 1, title: 1 },
fields: {
title: 1, state: 1, index: 1, workspace: 1,
},
},
).fetch();
}
return {
user,
ready,
categories,
};
}, Categories);
I am completly clueless what is going wrong, maybe somebody could help me out
React components are functions, and this error message comes about because you are passing in an object rather than a function.
That may be enough for you to find the problem now. It is helpful for us if you can do some narrowing down of the problem, rather than dumping a large amount of code - it becomes too expensive (time consuming) for us to help you if we have to pore through hundreds of lines of code.

Categories