Requirement In Material UI, using pagination by click on the digits e.g 1, 2, it should do api call with limit=10 and offset=from 10(after the first call)
**issue ** it working but I need to click two times on the digits, but functionality is working properly as it should.
By clicking on the digits or left/right arrow of pagination, calling handlePageChange method and setting offset state value and putted the offset as useEffect() dependency array so on every change it should make api call with new offset value
/*eslint-disable*/
import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import PerfectScrollbar from 'react-perfect-scrollbar';
import DeleteIcon from '#material-ui/icons/Delete';
import DescriptionIcon from '#material-ui/icons/Description';
import { useNavigate } from 'react-router-dom';
import Pagination from '#material-ui/lab/Pagination';
import {
Box,
Card,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
createTheme,
Tooltip,
IconButton
} from '#material-ui/core';
import usersServices from 'src/services/usersServices';
const CustomerListResults = (props) => {
const navigate = useNavigate();
const handleExportData = (value) => {
console.log('Value inside handleExportData: ', value);
navigate('/app/exports_device_data', { state: value });
};
const handleDeleteUser = (user) => {
const userId = user._id;
console.log('DELETE USER', userId);
const newUsers = props.users.filter((item) => item._id !== userId);
props.newUsers(newUsers, userId);
};
// --------------------------------Pagination-------------------------------
let limit = 10;
const [users, setUsers] = useState([]);
const [page, setPage] = useState(0);
const [count, setCount] = useState(0);
const [offSet, setOffSet] = useState(0);
const retrieveCustomers = async () => {
if (!count) {
const res = await usersServices.getAllUsersRecordCount();
let dataCnt = res.data.data;
setCount(Math.ceil(dataCnt / 10));
}
console.log('CHEKCING useEffect(): ');
const res = await usersServices.getAllUsers(limit, offSet);
setUsers(res.data.data);
};
const handlePageChange = (event, value) => {
setPage(value);
setOffSet(page * 10);
};
useEffect(retrieveCustomers, [offSet]);
console.log('CHECKING FOR offSet Value: ', offSet);
return (
<Card>
<PerfectScrollbar>
<Box sx={{ minWidth: 64 }}>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Phone</TableCell>
<TableCell>Type</TableCell>
<TableCell>Status</TableCell>
{/* <TableCell>Data Count</TableCell> */}
<TableCell>Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow hover key={user.email}>
<TableCell>{`${user.first_name} ${user.last_name}`}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>{user.phone_number}</TableCell>
<TableCell>{user.user_type}</TableCell>
<TableCell>{user.status}</TableCell>
{/* <TableCell>{user.device_data_count}</TableCell> */}
<TableCell>
<Tooltip title="Delete">
<IconButton
aria-label="delete"
onClick={() => handleDeleteUser(user)}
>
<DeleteIcon />
</IconButton>
</Tooltip>
<Tooltip title="Export">
<IconButton
aria-label="export data"
onClick={() => handleExportData(user)}
>
<DescriptionIcon />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</PerfectScrollbar>
<Pagination
component="div"
count={count}
page={page}
siblingCount={1}
boundaryCount={1}
variant="outlined"
// shape="rounded"
onChange={handlePageChange}
/>
</Card>
);
};
CustomerListResults.propTypes = {
users: PropTypes.array.isRequired
};
export default CustomerListResults;
Please help me with this, Thanks in advance
Try this:
const [page, setPage] = useState(1);
Related
I'm using Next.js. In Nav.js I have these consts:
export default function NestedList() {
const [value,setValue]=React.useState();
const theme=useTheme();
const isMatch=useMediaQuery(theme.breakpoints.down('lg'));
const [on, seton] = useState(false);
const [photos, setPhotos] = useState([]);
const [bea, setBea] = useState([]);
const [home, setHome] = useState([]);
const [aaa, setaaa] = useState(0);
const [bbb, setbbb] = useState(0);
const [ccc, setccc] = useState(0);
//I did not show the return function.
}
and in Drawerr.js I have this code:
import {useState} from 'react'
import { styled } from '#mui/material/styles';
import {Drawer, IconButton, List, ListItemButton, ListItemIcon, ListItemText,Box} from "#mui/material";
import MenuRoundedIcon from "#mui/icons-material/MenuRounded";
import Image from 'next/image';
import { height } from '#mui/system';
import Nav from './Nav';
const Drawerr = ({linkArray}) => {
const [open, setopen] = useState(false)
return (
<>
<Drawer PaperProps={{sx:{backgroundColor:'#035445'}}} open={open} onClose={()=> setopen(false)}>
<List>
<Box component="img" src="/dlogo.svg" sx={{ mt:-1, height:125,width:250}}/>
{linkArray.map((link,index)=>(
<ListItemButton divider key={index} onClick={()=>setopen(false)} >
<ListItemIcon >
<ListItemText sx={{ color:"#00ffd0" }} onClick={()=>{
if(index==1 ){setaaa(aaa+30),setbbb(bbb==0),setccc(ccc==0)}
if(index==4 ){setaaa(aaa==0),setccc(ccc==0),setbbb(bbb+30)}
if(index==0 ){setaaa(aaa==0),setbbb(bbb==0),setccc(ccc+30)}
}}>
{link}
</ListItemText>
</ListItemIcon>
</ListItemButton>
))}
</List>
</Drawer>
<IconButton onClick={()=>setopen(!open)} sx={{marginLeft:"auto" ,color:"#00a859"}}>
<MenuRoundedIcon/>
</IconButton>
</>
)
}
export default Drawerr
So I can't use those consts In Drawerr.js and I get this error:
ReferenceError: setaaa is not defined
What should I do to use those consts in Drawerr.js?
first thing
import Nav from './Nav';
here Nav is a function and not a object, now as per your code i can see you are trying impement custom hook? if i m right, you can create custom hook in react like this
export default function useNestedList() {
const [value,setValue]=React.useState();
const theme=useTheme();
const isMatch=useMediaQuery(theme.breakpoints.down('lg'));
const [on, seton] = useState(false);
const [photos, setPhotos] = useState([]);
const [bea, setBea] = useState([]);
const [home, setHome] = useState([]);
const [aaa, setaaa] = useState(0);
const [bbb, setbbb] = useState(0);
const [ccc, setccc] = useState(0);
//I did not show the return function.
return {
setaaa:setaaa,
setbbb:setbbb,
setccc:setccc,
aaa:aaa,
bbb:bbb,
ccc:ccc
}
}
and after that you can use this hook as this way in drawer.js
import {useState} from 'react'
import { styled } from '#mui/material/styles';
import {Drawer, IconButton, List, ListItemButton, ListItemIcon,ListItemText,Box} from "#mui/material";
import MenuRoundedIcon from "#mui/icons-material/MenuRounded";
import Image from 'next/image';
import { height } from '#mui/system';
import useNestedList from './Nav';
const Drawerr = ({linkArray}) => {
const [open, setopen] = useState(false)
const { setaaa,setbbb,setccc,aaa,bbb,ccc} =useNestedList()
return (
<>
<Drawer PaperProps={{sx:{backgroundColor:'#035445'}}} open={open} onClose={()=> setopen(false)}>
<List>
<Box component="img" src="/dlogo.svg" sx={{ mt:-1, height:125,width:250}}/>
{linkArray.map((link,index)=>(
<ListItemButton divider key={index} onClick={()=>setopen(false)} >
<ListItemIcon >
<ListItemText sx={{ color:"#00ffd0" }} onClick={()=>{
if(index==1 ){setaaa(aaa+30),setbbb(bbb==0),setccc(ccc==0)}
if(index==4 ){setaaa(aaa==0),setccc(ccc==0),setbbb(bbb+30)}
if(index==0 ){setaaa(aaa==0),setbbb(bbb==0),setccc(ccc+30)}
}}>
{link}
</ListItemText>
</ListItemIcon>
</ListItemButton>
))}
</List>
</Drawer>
<IconButton onClick={()=>setopen(!open)} sx={{marginLeft:"auto" ,color:"#00a859"}}>
<MenuRoundedIcon/>
</IconButton>
</>
)}
export default Drawerr
Hope this will help!
//StudentsFilter.jsx
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Badge,
Box,
Button,
Checkbox,
Flex,
Radio,
RadioGroup,
Text,
useColorMode,
useColorModeValue,
VStack,
} from "#chakra-ui/react";
import React, { useState } from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { axiosInstance } from "../../../axiosConfig";
import { commonSlidercss, darkSlidercss } from "../../../GlobalStyles";
import {
setFilterSearchMode,
setSearchingBatch,
setSearchingStream,
} from "../../../redux/slices/adminUserSlice";
const StudentsFilter = () => {
const [streams, setStreams] = useState(null);
const [batchYear, setBatchYear] = useState([]);
const [checkedStreams, setCheckedStreams] = useState([]);
const [checkedBatches, setCheckedBatches] = useState([]);
const siteState = useSelector((state) => state.siteReducer);
const adminUsers = useSelector((state) => state.adminUserReducer);
const filterSearchMode = adminUsers?.filterSearchMode;
const site = siteState.siteInfo;
const { colorMode } = useColorMode();
const dispatch = useDispatch();
useEffect(() => {
getStream();
let batches = [];
if (site) {
let year = site.year_established;
let current_year = new Date().getFullYear();
let year_diff = current_year - site.year_established;
for (let i = 0; i <= year_diff; i++) {
batches.push(year + i);
}
setBatchYear(batches);
}
}, [site]);
const getStream = async () => {
try {
const res = await axiosInstance.get("stream");
setStreams(res?.data?.stream);
} catch (error) {
console.log("Something went wrong while getting streams", e);
}
};
const streamsHandler = (e, li) => {
e.stopPropagation();
const index = checkedStreams.indexOf(li);
if (index > -1) {
setCheckedStreams([
...checkedStreams.slice(0, index),
...checkedStreams.slice(index + 1),
]);
} else {
setCheckedStreams([...checkedStreams, li]);
}
};
const batchesHandler = (e, li) => {
e.stopPropagation();
const index = checkedBatches.indexOf(li);
if (index > -1) {
setCheckedBatches([
...checkedBatches.slice(0, index),
...checkedBatches.slice(index + 1),
]);
} else {
setCheckedBatches([...checkedBatches, li]);
}
};
useEffect(() => {
dispatch(setSearchingStream(checkedStreams));
dispatch(setSearchingBatch(checkedBatches));
}, [checkedBatches, checkedStreams]);
return (
<Flex
p="6"
direction="column"
style={{ height: "inherit" }}
align="space-between"
justify="space-between"
w="300px"
maxH={231}
overflowY="scroll"
css={colorMode === "light" ? commonSlidercss : darkSlidercss}
>
<Box>
<Text fontWeight="medium" fontSize="sm" mb={7}>
More filters
</Text>
<Accordion allowMultiple>
<AccordionItem>
<AccordionButton>
<Box flex="1" fontSize="xs" textAlign="left">
Batch
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4}>
<RadioGroup>
<VStack align="start">
{batchYear &&
batchYear.map((li) => (
<Checkbox
// onChange={checkboxChange}
key={li}
value={li}
colorScheme={useColorModeValue(
"primaryScheme",
"purple"
)}
size="sm"
onChange={(e) => batchesHandler(e, li)}
isChecked={checkedBatches.includes(li)}
>
<Text fontSize="xs">{li}</Text>
</Checkbox>
))}
</VStack>
</RadioGroup>
</AccordionPanel>
</AccordionItem>
<AccordionItem>
<AccordionButton>
<Box flex="1" textAlign="left" fontSize="xs">
Stream
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4}>
<RadioGroup>
<VStack align="start">
{streams &&
streams.map((li) => (
<Checkbox
// onChange={checkboxChange}
key={li.id}
value={li.id}
colorScheme={useColorModeValue(
"primaryScheme",
"purple"
)}
size="sm"
onChange={(e) => streamsHandler(e, li.id)}
isChecked={checkedStreams.includes(li.id)}
>
<Text fontSize="xs">{li?.name}</Text>
</Checkbox>
))}
</VStack>
</RadioGroup>
</AccordionPanel>
</AccordionItem>
</Accordion>
</Box>
<Box>
<Button
width="full"
h="40px"
borderRadius="10px"
fontWeight="500"
variant="primary"
mt="10px"
onClick={() => dispatch(setFilterSearchMode(!filterSearchMode))}
>
Filter
</Button>
</Box>
</Flex>
);
};
export default StudentsFilter;
What is the reason why I am getting the error " React has detected a change in the order of Hooks called by StudentsFilter. This will lead to bugs and errors if not fixed" I have seen this warning in 2-3 components and also tried to correct it but I don't know what I am doing wrong? Can someone help me to identify it?
You're calling the useColorModeValue conditionally (and in a loop) in the return statement. That's probably the source of the error.
You should use ESLint and the "rules of hooks" rule, it would have been highlighted directly in your editor.
Now I have created this custom hook to perform lazy loading,which takes redux slice action as input and
import { useState, useEffect, useCallback, useRef } from "react";
import { useDispatch } from "react-redux";
function useLazyFetch(fetchAction) {
const dispatch = useDispatch();
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const loadMoreRef = useRef(null);
const handleObserver = useCallback(async(entries) => {
const [target] = entries;
console.log(target.isIntersecting);
if (target.isIntersecting) {
console.log("INTERSECTING.....");
await new Promise((r) => setTimeout(r, 2000));
setPage((prev) => prev + 1);
}
}, []);
useEffect(() => {
const option = {
root: null,
rootMargin: "0px",
threshold: 1.0,
};
const observer = new IntersectionObserver(handleObserver, option);
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
}, [handleObserver]);
const fetchApi = useCallback(async () => {
try {
setLoading(true);
await new Promise((r) => setTimeout(r, 2000));
dispatch(fetchAction(page))
setLoading(false);
} catch (err) {
console.error(err);
}
}, [page,fetchAction,dispatch]);
useEffect(() => {
fetchApi();
}, [fetchApi]);
return { loading, loadMoreRef };
}
export default useLazyFetch;
I am using this in my component like this, here you can see I am tracking div in the bottom using loadMoreRef from useLazyFetch, Now when I am commenting out the fetchApi(); from custom hook its working as expected, on scroll its logging INTERSECTING... in the console but the moment I try to execute the action through fetchApi() my whole app goes into loop,the div tracker with ref comes to top and it fetches the posts but after immediately that action repeats the tracker comes to top and page becomes empty & it fetches next set of posts,I can see that my list is getting appended new set of posts to state in redux dev tool instead of completely setting new state, but in UI it's rendering all posts again and again whic is causing the loop,how can I avoid this ?
import { CircularProgress, Grid, IconButton, Typography } from "#mui/material";
import { Box } from "#mui/system";
import React, { useEffect,useRef,useState } from "react";
import AssistantIcon from "#mui/icons-material/Assistant";
import Post from "../components/Post";
import { useDispatch, useSelector } from "react-redux";
import { getPosts } from "../redux/postSlice";
import AddPost from "../components/AddPost";
import useLazyFetch from "../hooks/useLazyFetch";
export default function Home() {
const dispatch = useDispatch();
// const api = `https://picsum.photos/v2/list`
const { status, posts } = useSelector((state) => state.post);
const {loading,loadMoreRef} = useLazyFetch(getPosts)
useEffect(() => {
dispatch(getPosts());
}, []);
return (
<Box>
<Box borderBottom="1px solid #ccc" padding="8px 20px">
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Typography variant="h6">Home</Typography>
</Grid>
<Grid item>
<IconButton>
<AssistantIcon />
</IconButton>
</Grid>
</Grid>
</Box>
<Box height="92vh" sx={{ overflowY: "scroll" }}>
<AddPost />
<Box textAlign="center" marginTop="1rem">
{status === "loading" && (
<CircularProgress size={20} color="primary" />
)}
</Box>
{status === "success" &&
posts?.map((post) => <Post key={post._id} post={post} />)}
<div style={{height:"50px",width:"100px",backgroundColor:"red"}} ref={loadMoreRef}>{loading && <p>loading...</p>}</div>
</Box>
</Box>
);
}
And here is my redux action & state update part
const initialState = {
status: "idle",
posts: []
};
export const getPosts = createAsyncThunk("post/getPosts", async (page) => {
console.log(page);
console.log("calling api ...");
const { data } = await axios.get(`/api/posts?page=${page}`);
return data;
});
export const postSlice = createSlice({
name: "post",
initialState,
reducers: {},
extraReducers: {
[getPosts.pending]: (state, action) => {
state.status = "loading";
},
[getPosts.fulfilled]: (state, action) => {
state.status = "success";
state.posts = [...state.posts,...action.payload.response.posts] ;
},
[getPosts.rejected]: (state, action) => {
state.status = "failed";
},
}
this is the solution that is working
import { CircularProgress, Grid, IconButton, Typography } from "#mui/material";
import { Box } from "#mui/system";
import React, { useEffect,useMemo } from "react";
import AssistantIcon from "#mui/icons-material/Assistant";
import Post from "../components/Post";
import { useDispatch, useSelector } from "react-redux";
import { getPosts } from "../redux/postSlice";
import AddPost from "../components/AddPost";
import useLazyFetch from "../hooks/useLazyFetch";
export default function Home() {
const { status, posts } = useSelector((state) => state.post);
const {loading,loadMoreRef} = useLazyFetch(getPosts)
const renderedPostList = useMemo(() => (
posts.map((post) => {
return( <Post key={post._id.toString()} post={post} />)
})
), [posts])
return (
<Box>
<Box borderBottom="1px solid #ccc" padding="8px 20px">
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Typography variant="h6">Home</Typography>
</Grid>
<Grid item>
<IconButton>
<AssistantIcon />
</IconButton>
</Grid>
</Grid>
</Box>
<Box height="92vh" sx={{ overflowY: "scroll" }}>
<AddPost />
<Box textAlign="center" marginTop="1rem">
{status === "loading" && (
<CircularProgress size={20} color="primary" />
)}
</Box>
{renderedPostList}
<div style={{height:"50px",width:"100px",backgroundColor:"red"}} ref={loadMoreRef}>{loading && <p>loading...</p>}</div>
</Box>
</Box>
);
}
}
I used useMemo hook to memoize and it works as expected
I'm building a shopping cart component and trying to format the cart layout. I just want to see the layout of the cart so I commented out the products component which should get me the cart layout alone, so I can see what it looks like and what's going on, however I keep getting the following error: "TypeError: Cannot read property 'length' of undefined" when I try to compile it in react.
My shopping cart component code:
import React from "react";
import { Container, Typography, Button, Grid } from "#material-ui/core";
import useStyles from "./styles";
const Cart = ({ cart }) => {
const isEmpty = !cart.line_items.length;
const classes = useStyles();
const EmptyCart = () => (
<Typography variant="subtitle1">No items selected</Typography>
);
const FilledCart = () => (
<>
<Grid container spacing={3}>
{cart.line_items.map((item) => (
<Grid item xs={12} sm={4} key={item.id}>
<div>{item.name}</div>
</Grid>
))}
</Grid>
<div className={classes.cardDetails}>
<Typography variant="h4">
Subtotal: {cart.subtotal.formatted_with_symbol}
</Typography>
<div>
<Button
className={classes.emptyButton}
size="large"
type="button"
variant="contained"
color="secondary"
>
Empty Cart
</Button>
<Button
className={classes.checkoutButton}
size="large"
type="button"
variant="contained"
color="primary"
>
Checkout
</Button>
</div>
</div>
</>
);
return (
<Container>
<div className={classes.toolbar} />
<Typography className={classes.title} variant="h3">
Your Shopping Cart
</Typography>
{isEmpty ? <EmptyCart /> : <FilledCart />}
</Container>
);
};
export default Cart;
The error is specifically on the line:
const isEmpty = !cart.line_items.length;
Also my App JS code:
mport React, { useState, useEffect } from "react";
import { Products, Navbar, Cart } from "./components";
import { commerce } from "./lib/commerce";
const App = () => {
const [products, setProducts] = useState([]);
const [cart, setCart] = useState({});
const fetchProducts = async () => {
const { data } = await commerce.products.list();
setProducts(data);
};
const fetchCart = async () => {
setCart(await commerce.cart.retrieve());
};
const handleAddToCart = async (productId, quantity) => {
const item = await commerce.cart.add(productId, quantity);
setCart(item.cart);
};
useEffect(() => {
fetchProducts();
fetchCart();
}, []);
console.log(cart);
return (
<div>
<Navbar totalItems={cart.total_items} />
{/*<Products products={products} onAddToCart={handleAddToCart} /> */}
<Cart cart={cart} />
</div>
);
};
export default App;
Any help is much appreciated.
Issue
There is no cart.line_items on the initial render since initial state is an empty object ({}).
const [cart, setCart] = useState({});
Solution
Provide valid initial state for the initial render so there's a truthy, defined cart.line_items object from which to have a length property, i.e. so !cart.line_items.length; can resolve to a value and not throw an error.
const [cart, setCart] = useState({ line_items: [] });
I have an upload file component, delete file component and files table component(presents the existing files in the system using axios) in the same page:
filesPage.js
import React from 'react';
import UploadFile from '../components/UploadFile'
import DeleteFile from '../components/DeleteFile'
import FilesTable from '../components/FilesTable'
function UploadFiles() {
return (
<div className="filesPage">
<UploadFile/>
<DeleteFile/>
<FilesTable/>
</div>
)
}
export default UploadFiles;
Now I want every time I upload new file or delete one, the files table will be updated which means after the axios post/delete, I need to rerender the files table component and do axios get again to get the active files.
Someone can help?
FilesTable.js
import React, {useState, useEffect} from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TablePagination from '#material-ui/core/TablePagination';
import TableRow from '#material-ui/core/TableRow';
import axios from 'axios';
function FilesTable() {
const [tfaArray, setTfaArray] = useState([]);
useEffect(() => {
axios.get("api/tfa").then((res) => setTfaArray(res.data)).catch((err) => console.log(err));
}, [])
const columns = [
{id: 'fileId', label: '#', minWidth: 100},
{id: 'name', label: 'name', minWidth: 100},
{id: 'date', label: 'upload date', minWidth: 100}
];
const rows = tfaArray.map((tfa, index) => ({fileId: (index + 1), name: tfa.fileName, date: tfa.dateTime.slice(0,24)}) )
const useStyles = makeStyles({
root: {
width: '50%',
position: 'absolute',
right: '10%',
top: '15%',
},
container: {
maxHeight: 322
},
headerCell: {
background: '#F5F5F5',
fontSize: '16px',
zIndex: '0'
}
});
const classes = useStyles();
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
return (
<>
<Paper className={classes.root}>
<TableContainer className={classes.container}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell className={classes.headerCell}
key={column.id}
style={{ minWidth: column.minWidth }}>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row, index) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={index}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id}>
{value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10, 15]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
</>
)
}
export default FilesTable;
Typically with React you do this by lifting state up as described in this React documentation.
In this case, you'd lift the state to the parent of these components, and have the FilesTable receive the list of files as a prop. (Props are basically component state managed by the parent rather than by the component itself.) Similarly the DeleteFile component would receive the function to call to delete a file, the UploadFile component would receive the function to use to add a file, etc.
Here's a simplified example:
const {useState} = React;
const Parent = () => {
const [files, setFiles] = useState([]);
const addFile = (file) => {
setFiles(files => [...files, file]);
};
const removeFile = (file) => {
setFiles(files => files.filter(f => f !== file));
};
return (
<div>
<FilesTable files={files} removeFile={removeFile} />
<UploadFile addFile={addFile} />
</div>
);
};
const FilesTable = ({files, removeFile}) => {
return (
<React.Fragment>
<div>{files.length === 1 ? "One file:" : `${files.length} files:`}</div>
<ul className="files-table">
{files.map(file => (
<li>
<span>{file}</span>
<span className="remove-file" onClick={() => removeFile(file)}>[X]</span>
</li>
))}
</ul>
</React.Fragment>
);
};
const UploadFile = ({addFile}) => {
const [file, setFile] = useState("");
const onClick = () => {
addFile(file);
setFile("");
};
return (
<div>
<input type="text" value={file} onChange={(e) => setFile(e.target.value)} />
<input type="button" value="Add" disabled={!file} onClick={onClick} />
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById("root"));
ul.files-table {
list-style-type: none;
}
.remove-file {
margin-left: 0.5rem;
cursor: pointer;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>