how to bind data in material-ui using redux hooks - javascript

I am using material-UI and redux in react I am using first-time redux so I am confused about how to implement it with my list items.
here is my code of product.js and I am successfully getting data in my redux store, I will show you below
const dispatch = useDispatch();
const {loading, product, error, productCount} = useSelector(state => state.products);
console.log(loading)
console.log(product)
console.log(error)
console.log(productCount)
useEffect(()=>{
const data = dispatch(getProducts());
console.log(data)
},[dispatch])
here is my code of table
<TableContainer component={Paper}>
<Table sx={{ minWidth: 500 }} aria-label="custom pagination table">
<TableBody>
{(rowsPerPage > 0
? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: rows
).map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell style={{ width: 160 }} align="right">
{row.calories}
</TableCell>
<TableCell style={{ width: 160 }} align="right">
{row.fat}
</TableCell>
</TableRow>
))}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
colSpan={3}
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: {
'aria-label': 'rows per page',
},
native: true,
}}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
if I show to my redux data so I am receiving data on this page
here is my console image
I hope it will be enough code to describe my problem but if anyone want to see further code to understand my problem so i will provide it

Related

How can I render the nested infos from products.productId?

How can I render the title and img of under the product.productId and throw it to the table?
I also tried to store the recenttransaction inside another state and then map it, but it still received the latest one. How can I store the projected information and throw it to the table down below.
So this is what I received.
{
"_id": "6361f27e499c60ef9ddb147c",
"products": [
{
"productId": {
"_id": "6360d7868044f3048e59bd8c",
"title": "shade",
"img": "https://firebasestorage.googleapis.com/v0/b/tua-ecom.appspot.com/o/1667291013451%5Bobject%20File%5D?alt=media&token=ea7fe952-6330-44fd-8b21-8fdc84ceb7d2"
},
"quantity": 1,
"sellerId": "6360d4d5bd860240e258c582",
"_id": "6361f27e499c60ef9ddb147d"
}
],
"amount": 10,
"location": "gym",
},
In this image, I can successfully render all information except the products.productId
So instead of rendering shade, I'm currently rendering the latest product added.
const [recentTransaction, setRecentTransaction] = useState([])
const [myProductTitle, setmyProductTitle] = useState([])
useEffect(() => {
const getStats = async () =>{
const res = await userRequest.get(`/order/recent/${id}`)
res.data.map((item) => {
item.products.map((i) => {
setmyProductTitle(i.productId)
})
})
setRecentTransaction(res.data)
setLoading(false)
}
getStats()
},[id])
This is how I render the table
{recentTransaction.map((recent) => (
<TableRow key={recent._id}>
<TableCell component="th" scope="row">
{recent._id}
</TableCell>
<TableCell align="left">{recent.userId.firstname}
{recent.userId.lastname}
</TableCell>
<TableCell align="left">{recent.userId.studentId}</TableCell>
<TableCell align="left">
<Box sx={{display: 'flex', alingItems:'center'}}>
<Typography mr={1}>{myProductTitle.title}</Typography>
<Box component="img" sx={{width: '50px'}} src{myProductTitle.img}/>
</Box>
</TableCell>
<TableCell align="left">₱ {recent.amount}</TableCell>
<TableCell align="left" sx={{display:'flex', alignItems:'center', borderRadius: '5px'}}>
<Typography sx={{padding:1, backgroundColor: 'yellow'}}>
{recent.status}
</Typography>
</TableCell>
</TableRow>
))}
If i understood correctly you need an iteration inside the table cell since products is an array, so for every element inside, print inside that cell the list of product along with their titles, images and ids.
Something like this
<TableCell align="left">
{recent.products.map(product => (
<Box sx={{display: 'flex', alingItems:'center'}}>
<Typography mr={1}>{product.productId.title}</Typography>
<Box component="img" sx={{width: '50px'}} src{product.productId.img}/></Box>
<span>{product.productId._id}</span>
</Box>
)}
</TableCell>

Pulling data out of a mapped array to calculate sums in react

I have 2 data sets that are currently being mapped over. the second data set is being mapped over inside of the first data set and rendering the values onto a modal for the user to view. I am however trying to take this second data set that is being mapped over out of its map sequence to render a sum total of each associated data point. For instance, I have a 'goalValue', an 'actualValue', and a 'result' value each of which belong to a specified category.
I should like to grab these data points when they are mapped over and push them out of the map into a separate array and or state to calculate a total that can be rendered at the bottom of the table in the modal itself. I created a separate component called ResultCalculator that takes in each value within the 'category.' Currently the state that i am trying to push the mapped values to is causing an infinite loop. I thought that maybe i could use a 'useEffect' but that isn't possible inside of a callback function.
I have added the code below for reference. Any potential insight would be greatly appreciated. I have been working on this for the last few hours whilst gaining no headway. Any search I've typed in on google has yet to render any valuable response.
Here is the code for the mappings.
const Reports = () => {
// declare const for grabbing user profile
const userProfile = () => {
axios.get('/api/users/profile', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('user')}`
}
}
)
.then(res => {
console.log(res.data)
setUsers({ ...users, months: res.data.months })
})
}
// BUTTON TO DELETE A MONTH
const handleDeleteMonth = (id) => {
console.log(id)
axios.delete(`/api/months/${id}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('user')}`
}
}).then(res => {
console.log(res)
userProfile()
})
}
//GRAB THE USER PROFILE
const [users, setUsers] = useState({ months: [] })
useEffect(() => {
userProfile()
}, [])
// defining modal styles
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 1000,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
// defining modal state
const [open, setOpen] = React.useState({});
const handleOpen = monthId => setOpen(prev => ({ ...prev, [monthId]: true }));
const handleClose = monthId => setOpen(prev => ({ ...prev, [monthId]: false }));
//RESULT CALCULATION STATES
const [actualsArray, setActualsArray] = useState(0)
// const [goalsResult, setGoalsResult] = useState(0)
// const [resultsResult, setResultsResult] = useState(0)
return (
<>
<NavBar></NavBar>
<br></br> <br></br>
<h1 style={{ color: "white", textAlign: "center", fontSize: "50px" }}>Budget Summaries</h1>
<br></br> <br></br>
<Container>
<Grid container spacing={2}>
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Month</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{users.months.map(month => (
<StyledTableRow key={month._id}>
<StyledTableCell component="th" scope="row">
<Button onClick={() => handleOpen(month._id)} align="left">{month.name}</Button>
<Button style={{ minWidth: 200 }} align="right" onClick={() => {handleDeleteMonth(month._id)}}>Delete</Button>
<Modal
open={open[month._id]}
onClose={() => handleClose(month._id)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography id="modal-modal-title" variant="h6" component="h2">
<Container>
< Grid container spacing={2} >
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Categories</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Spent</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Goals</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Results</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right"></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{month.categories.map(category => (
<>
<StyledTableRow key={category.name}>
<StyledTableCell component="th" scope="row">
{category.name}
</StyledTableCell>
<StyledTableCell align="right">{category.actualValue}</StyledTableCell>
<StyledTableCell align="right">{category.goalValue}</StyledTableCell>
<StyledTableCell align="right">{category.result}</StyledTableCell>
<StyledTableCell align="right"></StyledTableCell>
</StyledTableRow>
</>
))}
<StyledTableRow>
<styledTableCell component="th" scope="row">Net</styledTableCell>
<StyledTableCell align="right"><ResultCalculator/></StyledTableCell>
<StyledTableCell align="right"><ResultCalculator/></StyledTableCell>
<StyledTableCell align="right"><ResultCalculator/></StyledTableCell>
<StyledTableCell align="right"></StyledTableCell>
</StyledTableRow>
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid >
</Container >
</Typography>
</Box>
</Modal>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid>
</Container>
<br></br><br></br>
<hr style={{ color: "white" }}></hr>
<Footer></Footer>
</>
)
}
I also will include the exterior component called ResultCalculator. There are some things that are commented out because I am currently just trying to get 1 value to calculate correctly before trying the other 2.
> function ResultCalculator(actualValue, goalValue, result) {
>
> const [actualsResult, setActualsResult] = useState(0)
// const [goalsResult, setGoalsResult] = useState(0)
// const [resultsResult,setResultsResult] = useState(0)
>
> let actuals = 0
// let goals = 0
// let results = 0
>
> const actualCalculator = (actualValue) => {
> actuals = actuals + actualValue }
>
> // const goalCalculator = (goalValue) => {
// goals = goals + goalValue
// return goals
// }
>
> // const resultCalculator = (result) => {
// results = results + result
// return results
// }
actualCalculator(actualValue)
>
> // goalCalculator()
// resultCalculator()
useEffect(() => {
> setActualsResult(actuals)
}, [])
>
> // setGoalsResult(goals)
// setResultsResult(results)
>
> return (
> <>
> {actualsResult}
> {/* {goalsResult}
> {resultsResult} */}
> </> )
>
> }

trying to map data inside of already mapped data

data set 1 is an array, that contains another array data set 2. Data set 1 is currently mapped over to display a single column table with data1.name inside of it. data1.name is a button that can be clicked to display related data2.data. The current problem I am facing is that data sets that are unrelated to their respective parents are displaying in all the parent data locations. For instance, clicking data1[a] will display all the data2.data from data1[b] and so on and so forth. When i input a new data1, with new data2 nested. The data that is previously rendered in all the data1 sets is replaced by this new data.
I've attached my code in hopes that someone may be able to shed some light. Please beware that I am using an MUI library so the code is a bit bulky. There is a table, that maps over data1 to display each data1.name and then data.2 is mapped relatively immediately after presenting the user with a modal display of the data1.data2.data.
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.action.hover,
},
// hide last border
'&:last-child td, &:last-child th': {
border: 0,
},
}));
const Reports = () => {
//GRAB THE USER PROFILE
const [users, setUsers] = useState({ months: [] })
useEffect(() => {
axios.get('/api/users/profile', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('user')}`
}
}
)
.then(res => {
console.log(res.data)
setUsers({...users, months: res.data.months })
console.log(users.months)
})
}, [])
// defining modal styles
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 1000,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
// defining modal state
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<>
<NavBar></NavBar>
<br></br> <br></br>
<h1 style={{ color: "white", textAlign: "center", fontSize: "50px" }}>Budget Summaries</h1>
<br></br> <br></br>
<Container>
<Grid container spacing={2}>
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Month</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{users.months.map(month => (
<StyledTableRow key={month._id}>
<StyledTableCell component="th" scope="row">
<Button onClick={handleOpen}>{month.name}</Button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography id="modal-modal-title" variant="h6" component="h2">
<Container>
< Grid container spacing={2} >
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Categories</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Spent</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Goals</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Results</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right"></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{month.categories.map(category => (
<StyledTableRow key={category.name}>
<StyledTableCell component="th" scope="row">
{category.name}
</StyledTableCell>
<StyledTableCell align="right">{category.actualValue}</StyledTableCell>
<StyledTableCell align="right">{category.goalValue}</StyledTableCell>
<StyledTableCell align="right">{category.result}</StyledTableCell>
<StyledTableCell align="right"></StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid >
</Container >
</Typography>
</Box>
</Modal>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid>
</Container>
<br></br><br></br>
<hr style={{ color: "white" }}></hr>
<Footer></Footer>
</>
)
}
export default Reports
Please try replacing below code snippets as suggested:
1.
// defining modal state
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
with
// defining modal state
const [open, setOpen] = React.useState({});
const handleOpen = monthId => setOpen(prev => ({...prev, [monthId]: true}));
const handleClose = monthId => setOpen(prev => ({...prev, [monthId]: false}));
<Button onClick={handleOpen}>{month.name}</Button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
with
<Button onClick={() => handleOpen(month._id)}>{month.name}</Button>
<Modal
open={open[month._id]}
onClose={() => handleClose(month._id)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
Explanation
The current code has only one state-variable named open which gets set to true any time user clicks on the month.name button.
When it is set to true, all of the modals which have <Modal open={open}... will be rendered/displayed
The changes suggested above will make open an object of the form { monthId1: false, monthId2: true, ......}
Changes to handleOpen and handleClose will ensure only a particular month's open/close flag is updated
Replacing <Modal open={open} .... with <Modal open={open[month._id]} ...., ensures that each modal uses the true/false specific to the month._id.
NOTE: If month._id may be duplicated, then use a different unique attribute. If no such attribute exists, then replace {users.months.map(month => ( with {users.months.map((month, monthIndex) => ( and use monthIndex.
Observation / Note
We may avoid handleOpen, handleClose methods by using a simple flip method like so:
const flip = monthId => setOpen(prev => ({...prev, [monthId]: !prev[monthId]}));
So, both onClick and onClose will become something like so:
{() => flip(month._id)}

How do I map over data from API in a collapsible table?

I don't know if the question captures what I had in my but I will explain below...
I fetched data from API and mapped into a collapsible table. Full details of the data should be embedded in EACH row such that onclick on each row, reveals the full details. Here's the code below
function Encounter() {
const [open2, setOpen2] = useState(false);
const [details, setDetails] = useState([]);
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
setDetails(fetch.data.data)
} catch (err) {
console.log(err);
}
};
useEffect(() => {
getDetails();
}, []);
return (
<Wrapper>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<TableCell/>
<TableCell >
Enrollment ID
</TableCell>
<TableCell >
Encounter
</TableCell>
<TableCell>
Facility Code
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{details.map((detail, idx) => {
return (
<>
<TableRow sx={{ "& > *": { borderBottom: "unset" } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell key={idx}>
{detail.enrollment_id}
</TableCell>
<TableCell key={idx}>
{detail.encounter}
</TableCell>
<TableCell key={idx}>
{detail.faciity_code}
</TableCell>
</TableRow>
<TableRow>
<TableCell
style={{ paddingBottom: 0, paddingTop: 0 }}
colSpan={6}
>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1 }}>
<Typography variant="h6" gutterBottom component="div">
Details
</Typography>
<Tooltip />
</Box>
</Collapse>
</TableCell>
</TableRow>
</>
);
})}
</TableBody>
</Table>
</Wrapper>
);
}
export default Encounter;
The problem I have is how to implement the open and setOpen state to individual row, also the Tooltip component(which is a the full table details from the API) to display full details of each row onclick which should correspond to the selected row in question.
Here are two solutions to the first problem.
1. Create a separate component for each <TableRow />
This component will have its own state and allows you to collapse/expand each row individually.
2. Use a dictionary for the open state
Since you have multiple (dynamic) rows, you can introduce a dictionary for the open state.
const [open, setOpen] = useState({});
For each row, you will use the open[idx] property to determine if the row should be "open"
<Collapse in={open[idx]} timeout="auto" unmountOnExit>
And in the <IconButton /> component, set the state based on the current row state.
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(current => ({ ...current, [idx]: !current[idx] }))}
>
Firstly add a variable in your details object:
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
const response = fetch.data.data;
response.map((elem) => elem.open = false)
setDetails(response)
} catch (err) {
console.log(err);
}
};
Then you can change the open variable for each element in details:
<IconButton
aria-label="expand row"
size="small"
onClick={() => detail.open = !detail.open)}
>
You might need to update the state, so change your onClick:
onClick={() => changeOpenStatus(idx))}
and the function:
const changeOpenStatus = (idx) => {
const newDetails = {...details}
newDetails[idx].open = !newDetails[idx].open;
setDetails(newDetails)
}

Error: Cannot read property 'map' of undefined (state) inside render method

In my project I have a table from react material ui. I am getting an error while filling this table when I use data from state to fill the table. The error is cannot read property 'map' of undefined. The line where it occurs will be marked in the code snippet below.
Here is my code (the error is in the JSX render method):
class AllBooks extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: []
};
}
componentDidMount() {
console.log('component mounted!');
axios.get('http://localhost:8080/all-books').then(allBooks => {
console.log(allBooks.data);
this.setState({
rows: allBooks
});
});
}
onAddButtonClick = (row) => {
console.log('onAddButton click!');
console.log(row);
//axios.post('http://localhost:8080/addBookToRentedBooks', { email: this.state.email,
title: this.state.title, message: this.state.message }).then();
}
test = () => {
console.log(this.state.rows.data);
this.state.rows.data.map(row => console.log(row));
}
render() {
return (
<div>
<Grid container spacing={3}>
<Grid item xs={6} sm={3}></Grid>
<Grid item xs={6} sm={6}>
<h1 style={{textAlign: 'center'}}>All Books</h1>
<br /><br />
<TableContainer component={Paper}>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell >Title</TableCell>
<TableCell align="right">Author</TableCell>
<TableCell align="right">ISBN</TableCell>
<TableCell align="right">Year</TableCell>
<TableCell align="center">Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.rows.data.map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">z</TableCell>
<TableCell align="right">z</TableCell>
<TableCell align="right">z</TableCell>
<TableCell align="center">
<IconButton onClick={ () => this.onAddButtonClick(row) }>
<AddCircleOutlineIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={6} sm={3}><button onClick={this.test}>click me</button></Grid>
</Grid>
</div>
)
}
}
I wonder why this.state.rows.data is marked as undefined because I added a simple button - when i click this button, the test method gets called and prints out this.state.rows correctly.
What did I do wrong?
Thanks for every help!
Because initial of state rows is []. so when fetching data this.state.rows.data will be undefined.
You can use optional chaining to check it has value or not before using map:
this.state.rows.data?.map(...)

Categories

Resources