When I make API call and store the response in useState, it shows the data while running console.log(), however when this state value is passed as an argument to another component which is supposed to take the data and mapped it to show the result, it gives me an error saying
" Cannot read properties of undefined (reading 'map')"
Can anyone help me figure out what's wrong with my code?
Edit - As Mr.Silva suggested below, I added {menFootwears &&
menFootwears.map((menFootwear)=> () )}
It no longer shows error anymore, however, it also doesn't show the
data even though the data shows as an output in console.log() in
Product.jsx whereas it shows undefined in MenShoes.jsx and WomenShoes.jsx
Here's my code for Product.jsx
import { useMediaQuery } from '#mui/material';
import { Box } from '#mui/system';
import React from 'react'
import { theme } from '../style/theme';
import MenShoes from './collections/MenShoes';
import WomenShoes from './collections/WomenShoes';
export const Products = () => {
const matchScreen = useMediaQuery(theme.breakpoints.down('md'))
const [isLoading, setIsLoading] = React.useState(true);
const [menFootwears, setMenFootwears] = React.useState([]);
const [womenFootwears, setWomenFootwears] = React.useState([]);
//Women FootWears
async function fetchWomenFootwear () {
setIsLoading(true)
await fetch('https://dummyjson.com/products/category/womens-shoes')
.then(response => response.json())
.then(response => setWomenFootwears(response.products))
setIsLoading(false);
}
//Men Footwears
async function fetchMenFootwear () {
setIsLoading(true)
await fetch('https://dummyjson.com/products/category/mens-shoes')
.then(response => response.json())
.then(response => setMenFootwears(response.products))
setIsLoading(false)
}
React.useEffect(()=> {
fetchWomenFootwear()
fetchMenFootwear()
}, [])
const handleProductCard = (id) => {
console.log('hello')
}
console.log( womenFootwears, menFootwears)
return (
<Box>
<WomenShoes data={womenFootwears} onclick={handleProductCard} loadingStatus={isLoading}/>
<MenShoes data={menFootwears} onclick={handleProductCard} loadingStatus={isLoading}/>
</Box>
)
}
Both WomenShoes and MenShoes are designed using the same code except for the API response array data.
MenShoes/WomenShoes.jsx
import { ShoppingCartSharp } from '#mui/icons-material';
import { Button, Card, CardActionArea, CardContent, CardMedia, Divider, Rating, Skeleton, Typography, useMediaQuery } from '#mui/material';
import { Box } from '#mui/system';
import React from 'react'
import { theme } from '../../style/theme';
export default function MenShoes({menFootwears, handleProductCard, isLoading}) {
const matchScreen = useMediaQuery(theme.breakpoints.down('md'))
return(
<Box pt={2} mt={4}>
<Divider variant='middle' sx={{
"&.MuiDivider-root": {
"&::before, &::after": {
borderTopColor:theme.palette.primary.light,
borderTopWidth:'thin',
borderTopStyle:'solid'
},
}
}}>
<Typography color={theme.palette.primary.main} variant={!matchScreen ? 'h3': 'h5'}>
Men Footwears Collections
</Typography>
</Divider>
<Box display='flex'
justifyContent='space-evenly'
alignItems='center'
flexWrap='wrap'
pt={2}
mt={2}
px={2}>
{menFootwears.map((menFootwear)=> (
<Card key={menFootwear.id}
sx={{maxWidth:335,
height:'auto',
marginTop:'3.5em',
flex:!matchScreen ? '0 0 45%' : '0 0 80%'
}}
elevation={4}
onClick={()=>{handleProductCard(menFootwear.id)}}>
<CardActionArea>
{isLoading ?
<>
<Skeleton variant='rectangular' width='335' height='220' animation='wave'/>
</> :
<CardMedia component='img'
height='220'
image={menFootwear.images[0]}/>}
<CardContent sx={{
textAlign:'center',
}}>
{ isLoading ?
<>
<Skeleton variant='h6' animation='wave'/>
</> :
<Typography gutterBottom variant='h6'
fontWeight='bold'
color={theme.palette.primary.main}>
{menFootwear.title}
</Typography>}
{isLoading ?
<>
<Skeleton variant='body2' animation='wave'/>
</> :
<Typography variant='body2' gutterBottom color={theme.palette.primary.dark}>
Brand : {menFootwear.brand}
</Typography>}
{ isLoading ?
<>
<Skeleton variant='h5' animation='wave'/>
</> :
<Typography variant='h5' gutterBottom color={theme.palette.primary.main}>
$ {menFootwear.price}
</Typography>}
<Rating
size='small'
name="rating"
value={menFootwear.rating}
readOnly/>
</CardContent>
</CardActionArea>
{ isLoading ?
<>
<Skeleton variant='rectangular' width='335' height='20' animation='wave'/>
</> :
<Button size='medium'
sx={{all:'unset',
textAlign:'center',
fontFamily:theme.typography.fontFamily,
fontSize:16,
width:'100%',
padding:'0.7em',
margin:0,
color:'white',
background:`linear-gradient(90deg, ${theme.palette.primary.main},transparent) ${theme.palette.tertiary.main}`,
transition:'background 0.5s',
'&:hover': {
background:theme.palette.secondary.main,
}
}}>
<span style={{display:'inline-flex', alignItems:'center'}}>
<ShoppingCartSharp size='small'/> Add to Cart
</span>
</Button>}
</Card>
))}
</Box>
</Box>
)
}
Check before using the map if the variable menFootwears is not undefined or if the array is empty.
{menFootwears && menFootwears.map(el => () )}
Related
I have data returned from the backend as an array that i want to populate on react component.
home.js
import Head from "next/head";
import Header from "../src/components/Header";
import * as React from 'react';
import { styled } from '#mui/material/styles';
import Box from '#mui/material/Box';
import Paper from '#mui/material/Paper';
import Grid from '#mui/material/Grid';
import TextField from '#mui/material/TextField';
import SendIcon from '#mui/icons-material/Send';
import Stack from '#mui/material/Stack';
import Button from '#mui/material/Button';
import getDupImages from "../src/services/getDupImages";
import { useState, useEffect } from 'react'
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
export default function Home({data}) {
let _data;
const fetchData = async () => {
_data = await getDupImages();
console.log("DATA>>>", _data);
return _data;
};
const submit = (event) => {
event.preventDefault();
fetchData();
}
return (
<>
<Head>
<title>De-Dup</title>
<link rel="icon" type="image/ico" href="/img/goals.ico" />
</Head>
<Header />
<section>
<Box sx={{ flexGrow: 1 }}>
<Grid container spacing={2}>
<Grid item xs={5}>
<Box
component="form"
sx={{
'& > :not(style)': { m: 1, width: '75ch' },
}}
noValidate
autoComplete="off"
>
<TextField id="outlined-basic" label="location path" variant="outlined" />
<Stack direction="row" spacing={2}>
<Button variant="contained" onClick={submit} endIcon={<SendIcon />}>
Submit
</Button>
</Stack>
</Box>
</Grid>
<Grid item xs={7}>
{data.map((d) => {
return (
<div>
{d.title}
</div>
);
})}
</Grid>
</Grid>
</Box>
</section>
</>
);
}
Error
1 of 1 unhandled error
Server Error
TypeError: Cannot read property 'map' of undefined
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Put the data in the component state and check if there actually is data before displaying it.
const [data, setData] = useState();
const fetchData = async () => {
setData(await getDupImages());
}
Then in your JSX:
{!!data && data.map(d => <div>{d.title}</div>}
You are trying to render the data before it is available. Use this instead
{data && data.map((d) => {
return (
<div>
{d.title}
</div>
);
})}
Either initialise the data state as an array or use the Optional chaining (?.) operator before the map function:
data?.map((d) => {
return <div>{d.title}</div>;
})
Hope this helps.
I am trying to fetch data with RTK Query in next.js project and everything were fine until I had to fetch some more data from /api/exams endpoint. I have fetched almost everything from that endpoint and i know that's working fine but i still can't fetch some data from it. I'll provide screenshots of all code that's related to it. ok so here is the code where endpoints are:
Then let's continue with exams-response where i define body of endpoint:
Now I will provide code in my custom hook where i import that data from api/exams endpoint query:
And now i will show code of the actual page where i use them and where i think problem may lie also with another file which i will provide after this:
import { memo } from "react"
import { useIntl } from "react-intl"
import Stack from "#mui/material/Stack"
import Typography from "#mui/material/Typography"
import { ExamoTypesEnum } from "src/common/types/examo-types-enum"
import { rolesEnum } from "src/core/roles-enum"
import { useExamAssign } from "src/features/exams/hooks/use-exam-assign"
import { useExams } from "src/features/exams/hooks/use-exams"
import { useStartExam } from "src/features/exams/hooks/use-start-exam"
import { useIsMobile } from "src/helpers/use-is-mobile"
import { useAppSelector } from "src/redux/hooks"
import { ExamoCard } from "src/ui/examo/examo-card"
import { ExamoCardsGrid } from "src/ui/examo/examo-cards-grid"
import { ExamoHeader } from "src/ui/examo/examo-header"
import { CustomizedDialogs } from "src/ui/filter"
import { LoadingSpinner } from "src/ui/loading-spinner"
import { styled } from "src/ui/theme"
import { UnauthenticatedComponent } from "src/ui/unauthenticated"
import { useTags } from "../tags/hooks/use-tags"
import { useActiveExamQuery } from "./api"
const Ourbox = styled.div`
display: flex;
justify-content: space-between;
`
export const ExamsPage = memo(() => {
const isMobile = useIsMobile()
const userRole = useAppSelector((state) => state.auth.role)
const intl = useIntl()
const {
exams,
isLoadingExams,
selectedTags,
setSelectedTags,
checkedFilterTags,
setCheckedFilterTags,
} = useExams()
const { availableTags } = useTags()
const isLoadingAnExam = useAppSelector((state) => state.exam.isLoadingAnExam)
const { startAsync } = useStartExam()
const { data: activeExam, isFetching: isFetchingActiveExam } =
useActiveExamQuery(undefined, { refetchOnMountOrArgChange: 1 })
if (userRole === rolesEnum.None) {
return <UnauthenticatedComponent />
}
return (
<Stack sx={{ paddingX: isMobile ? 3 : "10vw", paddingY: 4 }} gap={4}>
<Ourbox>
<ExamoHeader
header={intl.formatMessage({
id: "exams-header",
defaultMessage: "Choose your exam",
})}
subtitle={intl.formatMessage({
id: "exams-header-subtitle",
defaultMessage:
"Our operators make quizzes and tests to help you upgrade and test your skills.",
})}
/>
<CustomizedDialogs
id="exams-page-filter"
selectedTags={selectedTags}
setSelectedTags={setSelectedTags}
availableTags={availableTags || []}
checkedFilterTags={checkedFilterTags}
setCheckedFilterTags={setCheckedFilterTags}
/>
</Ourbox>
{isLoadingExams && <LoadingSpinner />}
{!isLoadingExams && (!exams || exams.length === 0) && (
<Typography>
{intl.formatMessage({
id: "no-exams-available",
defaultMessage: "No exams available",
})}
</Typography>
)}
{exams && exams.length > 0 && (
<ExamoCardsGrid>
{exams.map((exam) => (
<ExamoCard
key={exam.id}
type={ExamoTypesEnum.EXAM}
useAssign={useExamAssign}
isStartButtonDisabled={
isLoadingAnExam ||
isFetchingActiveExam ||
(activeExam?.exam?.id !== undefined &&
exam.id !== activeExam.exam.id)
}
isResuming={
activeExam?.exam?.id !== undefined &&
exam.id === activeExam.exam.id
}
handleStart={() => startAsync(exam.id)}
isLoading={isLoadingAnExam}
title={exam.title}
duration={exam.duration}
tags={[
...new Set(exam.templates?.flatMap((et) => et.tags) || []),
]}
numberOfQuestions={exam.templates.reduce(
(total, current) => total + current.numberOfQuestions,
0,
)}
deadline-start={exam["deadline-start"]}
deadline-end={exam["deadline-end"]}
i={exam.id}
/>
))}
</ExamoCardsGrid>
)}
</Stack>
)
})
and the last one which is as mapped through in above the code. so it's :
import { memo } from "react"
import * as React from "react"
import { useIntl } from "react-intl"
import AccessTimeIcon from "#mui/icons-material/AccessTime"
import ExpandMoreIcon from "#mui/icons-material/ExpandMore"
import MoreHorizIcon from "#mui/icons-material/MoreHoriz"
import Box from "#mui/material/Box"
import Card from "#mui/material/Card"
import MuiChip from "#mui/material/Chip"
import Collapse from "#mui/material/Collapse"
import Grid from "#mui/material/Grid"
import IconButton, { IconButtonProps } from "#mui/material/IconButton"
import Stack from "#mui/material/Stack"
import { styled } from "#mui/material/styles"
import Typography from "#mui/material/Typography"
import { ExamoTypesEnum } from "src/common/types/examo-types-enum"
import { useAssignType } from "src/common/types/use-assign-type"
import { useAppSelector } from "src/redux/hooks"
import { ExamoAssignTo } from "src/ui/examo/examo-assign-to"
import { ExamoStartDialogBtn } from "src/ui/examo/examo-start-btn"
interface props {
duration: string | null
title: string
tags: string[] | null
numberOfQuestions: number | null
isLoading: boolean
handleStart: () => void
useAssign: useAssignType
isStartButtonDisabled: boolean
isResuming: boolean
type: ExamoTypesEnum
i: number
"deadline-start": string
"deadline-end": string
}
export const ExamoCard = memo(
({
duration,
tags,
title,
isLoading,
numberOfQuestions,
isStartButtonDisabled,
isResuming,
type,
handleStart,
useAssign,
i,
"deadline-end": deadlineEnd,
"deadline-start": deadlineStart,
}: props) => {
console.log(deadlineStart)
const intl = useIntl()
const user = useAppSelector((state) => state.auth)
const durationHours = duration?.split(":")[0]
const durationMinutes = duration?.split(":")[1]
// const [expanded, setExpanded] = React.useState(false)
const [expandedId, setExpandedId] = React.useState(-1)
// const handleExpandClick = () => {
// setExpanded(!expanded)
// }
const handleExpandClick = (i: number) => {
setExpandedId(expandedId === i ? -1 : i)
}
const preventParentOnClick = (event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation()
}
interface ExpandMoreProps extends IconButtonProps {
expand: boolean
}
const ExpandMore = styled((props: ExpandMoreProps) => {
const { expand, ...other } = props
return <IconButton {...other} />
})(({ theme, expand }) => ({
transform: !expand ? "rotate(0deg)" : "rotate(180deg)",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shortest,
}),
}))
return (
<Grid item xs={12} lg={6}>
<Card
onClick={() => handleExpandClick(i)}
aria-expanded={expandedId === i}
elevation={2}
sx={{
padding: "1rem",
height: "100%",
borderRadius: "1rem",
border: "solid 1px var(--palette-grey-400)",
transition: "all 0.1s ease-in-out",
":hover": {
backgroundColor: "var(--palette-grey-100)",
cursor: "pointer",
},
}}
>
<Stack direction="row" justifyContent="space-between">
<Stack
gap={numberOfQuestions ? 1.5 : 6}
sx={{
width: "100%",
}}
>
<Stack
direction="row"
sx={{
justifyContent: "space-between",
}}
>
<Typography
variant="h5"
sx={{ whiteSpace: "pre-wrap", wordBreak: "break-all" }}
>
{title}
</Typography>
<ExpandMore expand={expandedId === i}>
<ExpandMoreIcon />
</ExpandMore>
<Stack
direction="row"
gap={1}
sx={{
justifyContent: "flex-end",
alignItems: "center",
marginTop: "-1.75rem",
}}
>
<AccessTimeIcon />
<Typography
whiteSpace="nowrap"
variant="h6"
>{`${durationHours}h ${durationMinutes}m`}</Typography>
</Stack>
</Stack>
<Collapse in={expandedId === i} timeout="auto" unmountOnExit>
<Stack direction="row" gap={4}>
{numberOfQuestions && (
<Typography variant="h6">
{`${numberOfQuestions} ${intl.formatMessage({
id: "questions",
defaultMessage: "Questions",
})}`}
</Typography>
)}
</Stack>
<Stack
direction="row"
sx={{ flexWrap: "wrap", gap: 1, marginTop: "1rem" }}
>
{tags?.map((t, index) => (
<MuiChip
key={index}
label={t}
variant="filled"
sx={{ fontWeight: "bold" }}
color="secondary"
size="small"
/>
))}
</Stack>
</Collapse>
</Stack>
<Stack
sx={{ marginTop: "-0.5rem" }}
justifyContent="space-between"
alignItems="center"
spacing={user.role === "Operator" ? 0.1 : 1}
>
<MoreHorizIcon sx={{ marginLeft: "2rem" }} />
<Box onClick={preventParentOnClick}>
<Stack sx={{ marginLeft: "1.5rem" }}>
{user.role === "Operator" && (
<ExamoAssignTo useAssign={useAssign} title={title} />
)}
</Stack>
</Box>
<Box onClick={preventParentOnClick}>
<ExamoStartDialogBtn
type={type}
isResuming={isResuming}
handleStart={handleStart}
isLoading={isLoading}
isDisabled={isStartButtonDisabled}
/>
</Box>
</Stack>
</Stack>
</Card>
</Grid>
)
},
)
To sum up guys, I want to also fetch deadlineStart and deadlineEnd but i can't. I think problem is in last file or second to last, because maybe am not defining them properly in interface props in last code. And also i almost forgot to mention that when I try to console.log(deadlineStart) in the last code it says undefined in the browser. Edited Network Pic :
here is screenshot when i console log single exams :
I have a dynamically generated set of dropdowns and accordions that populate at render client-side (validated user purchases from db).
I'm running into an error that I'm sure comes from my menu anchorEl not knowing 'which' menu to open using anchorEl. The MUI documentation doesn't really cover multiple dynamic menus, so I'm unsure of how to manage which menu is open
Here is a pic that illustrates my use-case:
As you can see, the menu that gets anchored is actually the last rendered element. Every download button shows the last rendered menu. I've done research and I think I've whittled it down to the anchorEl and open props.
Here is my code. Keep in mind, the data structure is working as intended, so I've omitted it to keep it brief, and because it's coming from firebase, I'd have to completely recreate it here (and I think it's redundant).
The component:
import { useAuth } from '../contexts/AuthContext'
import { Accordion, AccordionSummary, AccordionDetails, Button, ButtonGroup, CircularProgress, ClickAwayListener, Grid, Menu, MenuItem, Typography } from '#material-ui/core'
import { ExpandMore as ExpandMoreIcon } from '#material-ui/icons'
import LoginForm from '../components/LoginForm'
import { motion } from 'framer-motion'
import { useEffect, useState } from 'react'
import { db, functions } from '../firebase'
import styles from '../styles/Account.module.scss'
export default function Account() {
const { currentUser } = useAuth()
const [userPurchases, setUserPurchases] = useState([])
const [anchorEl, setAnchorEl] = useState(null)
const [generatingURL, setGeneratingURL] = useState(false)
function openDownloads(e) {
setAnchorEl(prevState => (e.currentTarget))
}
function handleClose(e) {
setAnchorEl(prevState => null)
}
function generateLink(prefix, variationChoice, pack) {
console.log("pack from generate func", pack)
setGeneratingURL(true)
const variation = variationChoice ? `${variationChoice}/` : ''
console.log('link: ', `edit-elements/${prefix}/${variation}${pack}.zip`)
setGeneratingURL(false)
return
if (pack.downloads_remaining === 0) {
console.error("No more Downloads remaining")
setGeneratingURL(false)
handleClose()
return
}
handleClose()
const genLink = functions.httpsCallable('generatePresignedURL')
genLink({
fileName: pack,
variation: variation,
prefix: prefix
})
.then(res => {
console.log(JSON.stringify(res))
setGeneratingURL(false)
})
.catch(err => {
console.log(JSON.stringify(err))
setGeneratingURL(false)
})
}
useEffect(() => {
if (currentUser !== null) {
const fetchData = async () => {
// Grab user products_owned from customers collection for user UID
const results = await db.collection('customers').doc(currentUser.uid).get()
.then((response) => {
return response.data().products_owned
})
.catch(err => console.log(err))
Object.entries(results).map(([product, fields]) => {
// Grabbing each product document to get meta (title, prefix, image location, etc [so it's always current])
const productDoc = db.collection('products').doc(product).get()
.then(doc => {
const data = doc.data()
const productMeta = {
uid: product,
title: data.title,
main_image: data.main_image,
product_prefix: data.product_prefix,
variations: data.variations
}
// This is where we merge the meta with the customer purchase data for each product
setUserPurchases({
...userPurchases,
[product]: {
...fields,
...productMeta
}
})
})
.catch(err => {
console.error('Error retrieving purchases. Please refresh page to try again. Full error: ', JSON.stringify(err))
})
})
}
return fetchData()
}
}, [currentUser])
if (userPurchases.length === 0) {
return (
<CircularProgress />
)
}
return(
currentUser !== null && userPurchases !== null ?
<>
<p>Welcome, { currentUser.displayName || currentUser.email }!</p>
<Typography variant="h3" style={{marginBottom: '1em'}}>Purchased Products:</Typography>
{ userPurchases && Object.values(userPurchases).map((product) => {
const purchase_date = new Date(product.purchase_date.seconds * 1000).toLocaleDateString()
return (
<motion.div key={product.uid}>
<Accordion style={{backgroundColor: '#efefef'}}>
<AccordionSummary expandIcon={<ExpandMoreIcon style={{fontSize: "calc(2vw + 10px)"}}/>} aria-controls={`${product.title} accordion panel`}>
<Grid container direction="row" alignItems="center">
<Grid item xs={3}><img src={product.main_image} style={{ height: '100%', maxHeight: "200px", width: '100%', maxWidth: '150px' }}/></Grid>
<Grid item xs={6}><Typography variant="h6">{product.title}</Typography></Grid>
<Grid item xs={3}><Typography variant="body2"><b>Purchase Date:</b><br />{purchase_date}</Typography></Grid>
</Grid>
</AccordionSummary>
<AccordionDetails style={{backgroundColor: "#e5e5e5", borderTop: 'solid 6px #5e5e5e', padding: '0px'}}>
<Grid container direction="column" className={styles[`product-grid`]}>
{Object.entries(product.packs).map(([pack, downloads]) => {
// The pack object right now
return (
<Grid key={ `${pack}-container` } container direction="row" alignItems="center" justify="space-between" style={{padding: '2em 1em'}}>
<Grid item xs={4} style={{ textTransform: 'uppercase', backgroundColor: 'transparent' }}><Typography align="left" variant="subtitle2" style={{fontSize: 'calc(.5vw + 10px)'}}>{pack}</Typography></Grid>
<Grid item xs={4} style={{ backgroundColor: 'transparent' }}><Typography variant="subtitle2" style={{fontSize: "calc(.4vw + 10px)"}}>{`Remaining: ${downloads.downloads_remaining}`}</Typography></Grid>
<Grid item xs={4} style={{ backgroundColor: 'transparent' }}>
<ButtonGroup variant="contained" fullWidth >
<Button id={`${pack}-btn`} disabled={generatingURL} onClick={openDownloads} color='primary'>
<Typography variant="button" style={{fontSize: "calc(.4vw + 10px)"}} >{!generatingURL ? 'Downloads' : 'Processing'}</Typography>
</Button>
</ButtonGroup>
<ClickAwayListener key={`${product.product_prefix}-${pack}`} mouseEvent='onMouseDown' onClickAway={handleClose}>
<Menu anchorOrigin={{ vertical: 'top', horizontal: 'right' }} transformOrigin={{ vertical: 'top', horizontal: 'right' }} id={`${product}-variations`} open={Boolean(anchorEl)} anchorEl={anchorEl}>
{product.variations && <MenuItem onClick={() => generateLink(product.product_prefix, null, pack) }>{`Pack - ${pack}`}</MenuItem>}
{product.variations && Object.entries(product.variations).map(([variation, link]) => {
return (
<MenuItem key={`${product.product_prefix}-${variation}-${pack}`} onClick={() => generateLink(product.product_prefix, link, pack)}>{ variation }</MenuItem>
)
})}
</Menu>
</ClickAwayListener>
</Grid>
</Grid>
)}
)}
</Grid>
</AccordionDetails>
</Accordion>
</motion.div>
)
})
}
</>
:
<>
<p>No user Signed in</p>
<LoginForm />
</>
)
}
I think it also bears mentioning that I did check the rendered HTML, and the correct lists are there in order - It's just the last one assuming the state. Thanks in advance, and please let me know if I've missed something, or if I can clarify in any way. :)
i couldn't manage to have a menu dynamic,
instead i used the Collapse Panel example and there i manipulated with a property isOpen on every item of the array.
Check Cards Collapse Example
On the setIsOpen method you can change this bool prop:
const setIsOpen = (argNodeId: string) => {
const founded = tree.find(item => item.nodeId === argNodeId);
const items = [...tree];
if (founded) {
const index = tree.indexOf(founded);
founded.isOpen = !founded.isOpen;
items[index]=founded;
setTree(items);
}
};
<IconButton className={clsx(classes.expand, {
[classes.expandOpen]: node.isOpen,
})}
onClick={()=>setIsOpen(node.nodeId)}
aria-expanded={node.isOpen}
aria-label="show more"
>
<MoreVertIcon />
</IconButton>
</CardActions>
<Collapse in={node.isOpen} timeout="auto" unmountOnExit>
<CardContent>
<MenuItem onClick={handleClose}>{t("print")}</MenuItem>
<MenuItem onClick={handleClose}>{t("commodities_management.linkContainers")}</MenuItem>
<MenuItem onClick={handleClose}>{t("commodities_management.linkDetails")}</MenuItem>
</CardContent>
</Collapse>
I think this is the right solution for this: https://stackoverflow.com/a/59531513, change the anchorEl for every Menu element that you render. :D
This code belongs to TS react if you are using plain JS. Then remove the type.
import Menu from '#mui/material/Menu';
import MenuItem from '#mui/material/MenuItem';
import { useState } from 'react';
import { month } from '../../helper/Utilities';
function Company() {
const [anchorEl, setAnchorEl] = useState<HTMLElement[]>([]);
const handleClose = (event: any, idx: number) => {
let array = [...anchorEl];
array.splice(idx, 1);
setAnchorEl(array);
};
<div>
{month &&
month.map((val: any, ind: number) => {
return (
<div
key={val.id + 'w9348w344ndf allBankAndCardAccountOfClient'}
style={{ borderColor: ind === 0 ? '#007B55' : '#919EAB52' }}
>
<Menu
id='demo-positioned-menu'
aria-labelledby='demo-positioned-button'
anchorEl={anchorEl[ind]}
open={anchorEl[ind] ? true : false}
key={val.id + 'w9348w344ndf allBankAndCardAccountOfClient' + ind}
onClick={(event) => handleClose(event, ind)}
anchorOrigin={{
vertical: 'top',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
>
<MenuItem
key={val.id + 'w9348w344ndf allBankAndCardAccountOfClient' + ind}
onClick={(event) => handleClose(event, ind)}
style={{
display: ind === 0 ? 'none' : 'inline-block',
}}
>
<span
style={{
marginLeft: '.5em',
color: 'black',
background: 'inherit',
}}
>
Make Primary
</span>
</MenuItem>
<MenuItem onClick={(event) => handleClose(event, ind)}>
<span style={{ marginLeft: '.5em', color: 'black' }}>Edit</span>
</MenuItem>
<MenuItem
onClick={(event) => handleClose(event, ind)}
style={{
display: ind === 0 ? 'none' : 'inline-block',
}}
>
<span style={{ marginLeft: '.5em', color: 'red' }}>Delete</span>
</MenuItem>
</Menu>
</div>
);
})}
</div>;
}
export default Company;
I am trying to Update Material UI LinearProgressWithLabel progress value with my own Value. I am getting my value from on upload progress in the Axios.post method and it is the percent value below which is a number.
<Grid item xs>
<LinearWithValueLabel value={percent}/>
</Grid>
And then the Progress Bar:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import LinearProgress, { LinearProgressProps } from '#material-ui/core/LinearProgress';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
interface Props {
value: number;
}
const LinearProgressWithLabel: React.FC<Props> = ({ value }) => {
return (
<Box display="flex" alignItems="center">
<Box width="100%" mr={1}>
<LinearProgress variant="determinate" />
</Box>
<Box minWidth={35}>
<Typography variant="body2" color="textSecondary">{`${Math.round(
value,
)}%`}</Typography>
</Box>
</Box>
);
}
const useStyles = makeStyles({
root: {
width: '100%',
},
});
const LinearWithValueLabel: React.FC = () => {
const classes = useStyles();
const [progress, setProgress] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) => (prevProgress >= 100 ? 0 : progress));
}, 800);
return () => {
clearInterval(timer);
};
}, []);
return (
<div className={classes.root}>
<LinearProgressWithLabel value={progress} />
</div>
);
}
export default LinearWithValueLabel;
But I am getting this error in the console:
Warning: Material-UI: you need to provide a value prop when using the determinate or buffer variant of LinearProgress
What am I doing wrong?
You are missing the value prop.
const LinearProgressWithLabel: React.FC<Props> = ({ value }) => {
return (
<Box display="flex" alignItems="center">
<Box width="100%" mr={1}>
<LinearProgress variant="determinate" value={value} />
</Box>
<Box minWidth={35}>
<Typography variant="body2" color="textSecondary">{`${Math.round(
value,
)}%`}</Typography>
</Box>
</Box>
);
}
I'm having a devil of a time getting this component to properly take an array and map out components based on it. Although I can see this array log to the console, for some reason, my component (in the dev environment) is just displaying blank. So, I know that ternary statement is going to true, but React isn't then mapping (and thus, creating JSX) based on the array.
Note: I've checked out other questions here on SO, and I haven't seen one that's quite like this. If I'm wrong, please point me in that direction!
Expected Behavior
I am successfully setting listings to an array with 11 objects in it, so the DOM should display 11 cards using this data.
Observed Behavior
The DOM will display <Skeleton />s at first, indicating that the ternary statement is flowing to the falsey code block, and then the DOM will just not display anything. White space.
Code Sample
Browse (Parent)
export default function Browse({ user }) {
// prevents useEffect from infinite looping
let [loaded, setLoaded] = useState(false)
let [listings, setListings] = useState(false) // Raw listings array pulled from DB
let [radius, setRadius] = useState(10000)
useEffect(() => {
// Waits for user to come in from Auth Context
if (typeof user.userDoc !== 'undefined' || listings.length < 1) {
if (loaded === false) {
let grabListings = async () => await getLocalListings().then(result => setListings(result))
grabListings()
}
if (listings.length > 1) {
setLoaded(true)
}
}
}, [user, listings])
console.log(listings) // successfully shows array in listings state
return (
<>
<Navbar />
{
listings
? listings.map(listing => {
return (
<BrowseItem
... // props successfully passed here
/>
)
})
: <>
<Container style={{ marginTop: '18px' }}>
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
</Container>
</>
}
<Footer />
</>
)
}
BrowseItem (Child)
export default function BrowseItem({
creatorProfilePic,
creatorUsername,
docId,
mainImgUrl,
productStory,
productTitle,
seoTitle,
user
}) {
...
let [favorited, setFavorited] = useState(null) // whether user has favorited this item
let [loaded, setLoaded] = useState(false)
/** Handles click of the favorite button.
* Routes to FirebaseCalls function based on state of favorited
*/
const handleFavorite = async (e) => {
e.preventDefault()
let result
if (favorited) {
result = await removeFavorite(docId, user.currentUid)
if (result === 'success') {
setFavorited(!favorited)
setSnackMessage('Removed from Favorites')
setOpen(true)
} else {
setSnackMessage('⚠️ There was an error removing this from Favorites')
setOpen(true)
}
} else {
result = await addFavorite(docId, user.currentUid)
if (result === 'success') {
setFavorited(!favorited)
setSnackMessage('✓ Added to Favorites')
setOpen(true)
} else {
setSnackMessage('⚠️ There was an error adding this to Favorites')
setOpen(true)
}
}
}
useEffect(() => {
if (!loaded) {
// Determines whether item should default to favorited
try {
let userFavorites = user.userDoc.favorites
if (userFavorites) {
if (userFavorites.some(id => id === docId)) {
setFavorited(true)
}
}
setLoaded(true)
} catch (error) {
console.log(error)
}
}
}, [user])
////////////////////////////////////////////////////////////////////////
///////////////////////////// SNACKS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////
let [snackMessage, setSnackMessage] = useState('')
let [open, setOpen] = useState(null)
const handleSnackClose = (event, reason) => {
if (reason === 'clickaway') {
return
}
setOpen(false)
}
return (
<>
{
loaded
? <>
<Card>
<CardHeader
avatar={
<Avatar alt={`${creatorUsername} Profile Picture`} src={creatorProfilePic} />
}
action={
<IconButton>
<MoreHoriz />
</IconButton>
}
title={productTitle}
subheader={`${creatorUsername}`}
/>
<CardMedia
className={classes.media}
image={mainImgUrl}
title={productTitle}
onClick={handleImgTouch}
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
{productStory}
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton onClick={handleFavorite}>
{
favorited
? <Favorite color='primary' />
: <Favorite />
}
</IconButton>
<IconButton>
<Share />
</IconButton>
</CardActions>
</Card>
<Box mb={4}></Box>
</>
: <>
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
</>
}
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={open}
autoHideDuration={3000}
onClose={handleSnackClose}
message={snackMessage}
action={
<>
<Button onClick={handleFavorite} color='primary'>UNDO</Button>
<IconButton size="small" color="inherit" onClick={handleSnackClose}>
<Close fontSize="small" />
</IconButton>
</>
}
/>
</>
)
}
Stack
"next": "^9.4.4"
"react": "^16.8.6"
Testing in a Next.js Development Environment