Redux Toolkit Query not fetching data - javascript

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 :

Related

useEffect first render not setting the posts value to 10

I am making the infinite scroll in react js , but i am having problem setting the posts value whenever i refresh the page the posts length is 0 whereas it should have been 10.
But after the first reload if i don't reload and just change something in code ( lets say add console.log ) and save it then the whole infinite scroll starts working but if i reload then it stops working
Please help.
Feed.js
import { Box, CircularProgress, Stack, Typography } from "#mui/material";
import Post from "./Post";
import About from "./About";
import { useEffect, useRef, useState } from "react";
import { getInfiniteScroll } from "../apis/posts";
import { useNavigate } from "react-router-dom";
const Feed = () => {
// const posts = useSelector((state) => state.allPosts.posts);
const [posts, setPosts] = useState([]);
const [skip, setSkip] = useState(0);
const [isEnd, setIsEnd] = useState(false);
const ref = useRef();
useEffect(() => {
fetchPosts();
ref.current?.addEventListener("scroll", handleScroll);
// return () => ref.current?.removeEventListener("scroll", handleScroll);
}, [skip]);
const fetchPosts = async () => {
try {
const { data, error } = await getInfiniteScroll(skip);
if (error) {
console.log(error);
return;
}
if (data?.length === 0) {
setIsEnd(true);
return;
}
// setPosts(data);
setPosts([...posts, ...data]);
console.log(posts.length);
} catch (error) {
console.log(error.message);
}
};
const handleScroll = (e) => {
const { offsetHeight, scrollTop, scrollHeight } = e.target;
if (offsetHeight + scrollTop >= scrollHeight) {
console.log(posts?.length);
setSkip(posts?.length);
}
console.log("skip : ", skip);
};
return (
<Box flex={4} sx={{ padding: { xs: "0", sm: "0px 20px " } }}>
<Box
ref={ref}
// onScroll={handleScroll}
sx={{
width: { xs: "100%", sm: "105% " },
marginBottom: "50px",
height: "600px",
overflow: "scroll",
overflowX: "hidden",
}}>
{posts.length > 0 ? (
posts.map((post) => <Post key={post._id} {...post} />)
) : (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignSelf: "center",
marginTop: "200px",
}}>
<CircularProgress
sx={{
alignSelf: "center",
}}
/>
</Box>
)}
</Box>
<Box
sx={{
display: { sm: "none", xs: "block" },
justifyContent: "center",
alignItems: "center",
paddingBottom: "50px",
}}>
<About />
</Box>
</Box>
);
};
export default Feed;
Posts.js
import {
Avatar,
Card,
CardActions,
CardContent,
CardHeader,
CardMedia,
Checkbox,
IconButton,
} from "#mui/material";
import ShareIcon from "#mui/icons-material/Share";
import MoreVertIcon from "#mui/icons-material/MoreVert";
import Favorite from "#mui/icons-material/Favorite";
import { FavoriteBorder } from "#mui/icons-material";
import { useNavigate } from "react-router-dom";
import SmartText from "../Helpers/SmartText";
import { capitalize } from "../Helpers/Capitalize";
const Post = ({ _id, desc, title, photo, caption, updatedAt }) => {
const navigate = useNavigate();
return (
<Card sx={{ marginBottom: "20px" }}>
<CardHeader
avatar={
<Avatar sx={{ bgcolor: "red" }} aria-label="recipe">
{Array.from(title)[0]}
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={capitalize(title)}
subheader={updatedAt}
onClick={() => {
navigate("/posts/singlePost/" + _id);
}}
sx={{ cursor: "pointer" }}
/>
<CardMedia component="img" height="20%" image={photo} alt="Paella dish" />
<CardContent>
<SmartText text={desc} />
{/* <Typography variant="body2" color="text.secondary">
{post.desc}
</Typography> */}
</CardContent>
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<Checkbox
icon={<FavoriteBorder />}
checkedIcon={<Favorite sx={{ color: "red" }} />}
/>
</IconButton>
<IconButton aria-label="share">
<ShareIcon />
</IconButton>
{/* <ExpandMore
expand={expanded}
aria-expanded={expanded}
aria-label="show more"
>
<ExpandMoreIcon />
</ExpandMore> */}
</CardActions>
</Card>
);
};
export default Post;
api/posts.js
export const getInfiniteScroll = async (skip) => {
try {
const res = await fetch(`/api/posts/infiniteScroll?skip=${skip}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
return await res.json();
} catch (error) {
throw new Error(error);
}
};
backend/postController.js
exports.getInfinitePost = async (req, res) => {
const skip = req.query.skip ? Number(req.query.skip) : 0;
const DEFAULT_LIMIT = 10;
try {
const posts = await Post.find({}).skip(skip).limit(DEFAULT_LIMIT);
res.status(201).json({ data: posts, success: true });
} catch (error) {
res.status(400).json({ message: error });
}
};

Uncaught TypeError: setStatusFilters is not a function in react

So In my react application I have filtered option dialog. When a user select filter option and there is no related data, the dialog prompts No objects found based on your filter, try changing filter criteria and below this text I have a button called back to reset the filters. To do so I have defined a function resetAllFilters inside this function I called setStatusFilters.
But when I call resetAllFilters function I got Uncaught TypeError: setStatusFilters is not a function at resetAllFilters
Here is my code.
import { Search } from "#mui/icons-material";
import { Divider, Grid, List, Stack, Typography } from "#mui/material";
import React, { useEffect, useState, useMemo } from "react";
import TextInput from "../../../components/input/TextInput";
import Loading from "../../../components/loading/Loading";
import { filterObjects, filterSearch } from "../../../data/helpers/Helpers";
import TrackingFilterContainer from "../containers/TrackingFilterContainer";
import ObjectListItem from "./ObjectListItem";
import {
makeDefaultFilterState,
makeFilterOptionsObj,
} from "../../../data/helpers/Helpers";
import Button from "../../../components/button/Button";
const classes = {
Root: {
height: "100vh",
},
SearchInput: (theme) => ({
mt: "5%",
// ml: "5%",
p: 0.7,
borderRadius: "5px",
width: "100%",
bgcolor: "white",
"& input": {
overflow: "hidden",
textOverflow: "ellipsis",
},
"& svg": {
mr: "2.5%",
},
[theme.breakpoints.down("md")]: {
mt: 0,
pl: 1.5,
bgcolor: "background.primary",
},
}),
SearchLogo: { color: "misc.hint" },
Divider: (theme) => ({
mt: "2.5%",
width: "100%",
mb: 3,
[theme.breakpoints.down("md")]: {
display: "none",
},
}),
FilterWrapper: {
// mt: "3%",
px: "2%",
pt: "5%",
pb: "3%",
columnGap: "3%",
},
ListWrapper: {
// mt: "1.5%",
height: "100%",
overflow: "auto",
"&::-webkit-scrollbar": {
width: "3px",
},
"&::-webkit-scrollbar-thumb": {
borderRadius: "8px",
backgroundColor: "misc.hint",
},
},
};
function ObjectList({
search,
setSearch,
trackingObjects,
loading,
error,
selectedObj,
setSelectedObj,
statusFilters,
setStatusCheckboxFilters,
setStatusFilters,
}) {
const handleClick = React.useCallback((obj) => {
setSelectedObj(obj);
}, []);
console.log(statusFilters, "object");
const resultObjs = filterSearch(
filterObjects(trackingObjects, statusFilters),
search
);
const [res, setReset] = useState(false);
const reset = () => setReset(!setStatusFilters);
const objsWithLocations = resultObjs.filter(
({ location }) =>
location?.[0] !== null &&
location?.[0] !== undefined &&
location?.[1] !== null &&
location?.[1] !== undefined
);
const defaultFilterState = useMemo(
() => makeDefaultFilterState(trackingObjects),
[trackingObjects]
);
const filterOptionsObj = useMemo(
() => makeFilterOptionsObj(trackingObjects),
[trackingObjects]
);
const resetAllFilters = () => {
setStatusFilters([]);
};
// console.log('objsWithLocations', objsWithLocations);
return (
<Grid container direction="column" sx={classes.Root} wrap="nowrap">
<Stack
sx={{
flexDirection: { xs: "row", md: "column" },
alignItems: { xs: "center", md: "flex-start" },
justifyContent: { xs: "space-between", md: "flex-start" },
px: 1.0,
mb: 2,
}}
>
<TextInput
autoComplete="off"
variant="standard"
placeholder="Search for an object...."
value={search}
onValueChange={setSearch}
InputProps={{
startAdornment: <Search sx={classes.SearchLogo} />,
disableUnderline: true,
}}
sx={classes.SearchInput}
/>
<Divider sx={classes.Divider} />
<TrackingFilterContainer
defaultFilterState={defaultFilterState}
filterOptionsObj={filterOptionsObj}
/>
</Stack>
{loading && !resultObjs?.length && <Loading />}
{error && (
<Typography
variant="h5"
color="text.secondary"
mx="auto"
mt="5%"
width="45%"
textAlign="center"
>
{error}
</Typography>
)}
{!loading && objsWithLocations?.length === 0 ? (
<Typography
variant="h5"
color="text.secondary"
mx="auto"
mt="5%"
width="45%"
textAlign="center"
// onClick={() => defaultFilterState()}
>
No objects found based on your filter, try changing filter criteria
<Button
variant="text"
text="Back"
sx={{
fontSize: "24px",
p: 0,
verticalAlign: "baseline",
ml: "6px",
textTransform: "none",
}}
onClick={() => resetAllFilters()}
/>
</Typography>
) : (
<List sx={classes.ListWrapper}>
{objsWithLocations.map((obj) => (
<ObjectListItem
key={obj.trackingObjectId}
object={obj}
handleClick={handleClick}
isActive={selectedObj?.trackingObjectId === obj.trackingObjectId}
/>
))}
</List>
)}
</Grid>
);
}
export default ObjectList;
// export default React.memo(ObjectList);
Here is ObjectList.js rendered
import {Grid} from "#mui/material";
import {useNavigate} from "react-router-dom";
import {useTheme} from "#mui/material/styles";
import useMediaQuery from "#mui/material/useMediaQuery";
import React, {useEffect, useState} from "react";
import {TrackingPageItems} from "../../data/constants/TrackingObject";
import Map from "./map/Map";
import MobileNavigate from "./mobileNavigate/MobileNavigate";
import ObjectList from "./objectList/ObjectList";
import ObjectOne from "./objectOne/ObjectOne";
import buildSocketIoConnection from "../../data/socket.io/client";
import API from "../../data/api/API";
import {handleError} from "../../data/helpers/apiHelpers";
import {checkAuthStatus} from "../../data/helpers/Helpers";
import {filterObjects, filterSearch} from "../../data/helpers/Helpers";
function TrackingPage({
trackingObjects,
updateTrackingObjects,
setTrackingObjects,
selectedObject,
setSelectedObject,
...props
}) {
const [objectListSearch, setObjectListSearch] = useState("");
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
const [selectedTab, setSelectedTab] = useState(TrackingPageItems.LIST);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const navigate = useNavigate();
const handleTabChange = (newTab) => {
setSelectedTab(newTab);
};
useEffect(() => {
const userInfoString = localStorage.getItem("userInfo");
const userInfo = JSON.parse(userInfoString);
const token = userInfo?.token;
let possibleSocketIoConnection = null;
if (token) {
possibleSocketIoConnection = buildSocketIoConnection(token, updateTrackingObjects);
} else {
console.error("No token provided. Won't connect to socket.io server");
}
return () => {
setSelectedObject(null);
possibleSocketIoConnection?.closeConnection?.();
};
}, []);
const fetchObjectList = React.useCallback((firstTime = false) => {
firstTime && setLoading(true);
API.object
.getObjectList()
.then((objs) => {
// console.log('TrackingObjects', objs.data);
setTrackingObjects(objs.data);
})
.catch((err) => {
console.log(err);
checkAuthStatus(err.response?.status, navigate);
setError(handleError(err, "getObjectList"));
})
.finally(() => {
firstTime && setLoading(false)
});
}, []);
useEffect(() => {
fetchObjectList(!trackingObjects?.length);
// const interval = setInterval(fetchObjectList, 20000);
// return () => clearInterval(interval);
}, []);
const resultObjs = filterSearch(filterObjects(trackingObjects, props.statusFilters), objectListSearch);
return (
<>
<MobileNavigate selectedTab={selectedTab} handleTabChange={handleTabChange} />
<Grid container direction="row" spacing={1}>
{(!isMobile || (isMobile && selectedTab === TrackingPageItems.LIST)) && (
<Grid item container direction="column" xs={12} md={3}>
{selectedObject ? (
<ObjectOne trackingObjects={trackingObjects} selectedObj={selectedObject} setSelectedObj={setSelectedObject} />
) : (
<ObjectList
search={objectListSearch}
setSearch={setObjectListSearch}
selectedObj={selectedObject}
setSelectedObj={setSelectedObject}
trackingObjects={trackingObjects}
loading={loading}
error={error}
{...props}
/>
)}
</Grid>
)}
{(!isMobile || (isMobile && selectedTab === TrackingPageItems.MAP)) && (
<Grid item container direction="column" xs={12} md={9}>
<Map
loading={loading}
selectedObj={selectedObject}
setSelectedObj={setSelectedObject}
trackingObjects={resultObjs}
{...props}
/>
</Grid>
)}
</Grid>
</>
);
}
export default React.memo(TrackingPage);
How can I solve this error?

How to handle multiple menu state with Material-UI Menu component?

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;

How can I pass values or fields from one page to another page in ReactJS?

How can I send the value of the checkbox to the checkout.js page? This is the PaymentForm page. I tried my best but it's not working correctly. Basically, I want to use the PaymentForm fields in checkout.js page because my submit button is there.
PaymentForm.js
import React from 'react';
import Typography from '#material-ui/core/Typography';
import Grid from '#material-ui/core/Grid';
import TextField from '#material-ui/core/TextField';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import { createStyles } from '#material-ui/core/styles';
import StripeCheckout from 'react-stripe-checkout'
import 'react-toastify/dist/ReactToastify.css';
const styles = createStyles({
formControlLabel: {
fontSize: '1.5rem',
'& label': { fontSize: '5rem' }
}
});
const handleToken = (token) => {
console.log(token);
}
const PaymentForm = ({ PaymentForm, changePaymentForm }) => {
const [state, setState] = React.useState({
checkedA: false,
});
const handleChange = (event) => {
setState({ ...state, [event.target.name]: event.target.checked });
};
return (
<React.Fragment>
<Typography variant="h4" gutterBottom>
Payment method
</Typography><br />
<Grid container spacing={3}>
<Grid item xs={12}>
<FormControlLabel
control={<Checkbox checked={state.checkedA} onChange={handleChange} name="checkedA"/>}
label={<Typography style={styles.formControlLabel}>Cash on delivery</Typography>}
/>
</Grid>
<Grid item xs={12}>
<StripeCheckout
stripeKey="pk_test_51I9XPQAesAg2GfzQyVB7VgP0IbmWwgcfeFJSuCpB2kbNu60AFTbFhC7dxwje8YF4w2ILMJ6o2InB9ENczpd4dCSa00e09XoDbw"
token={handleToken}
amount={2 * 100}
name="All Products"
/>
</Grid>
</Grid>
</React.Fragment>
);
};
export default PaymentForm;
Checkout.js
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Paper from '#material-ui/core/Paper';
import Stepper from '#material-ui/core/Stepper';
import Step from '#material-ui/core/Step';
import StepLabel from '#material-ui/core/StepLabel';
import Button from '#material-ui/core/Button';
import Link from '#material-ui/core/Link';
import Typography from '#material-ui/core/Typography';
import axios from '../../axios-orders';
import AddressForm from './CheckoutForm';
import PaymentForm from './PaymentForm';
import Review from './Review';
const useStyles = makeStyles((theme) => ({
appBar: {
position: 'relative',
},
layout: {
width: 'auto',
marginLeft: theme.spacing(2),
marginRight: theme.spacing(2),
[theme.breakpoints.up(1000 + theme.spacing(2) * 2)]: {
width: 1100,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing(3),
marginBottom: theme.spacing(3),
padding: theme.spacing(2),
[theme.breakpoints.up(700 + theme.spacing(3) * 2)]: {
marginTop: theme.spacing(6),
marginBottom: theme.spacing(6),
padding: theme.spacing(3),
backgroundColor: 'rgb(248, 246, 244)',
},
},
stepper: {
padding: theme.spacing(5, 0, 5),
fontWeight: 'bold',
backgroundColor: 'rgb(248, 246, 244)',
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
},
button: {
marginTop: theme.spacing(3),
marginLeft: theme.spacing(1),
border: "none"
},
}));
const steps = ['Shipping address', 'Payment details', 'Review your order'];
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '}
<Link color="inherit" href="https://material-ui.com/">
Your Website
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
}
function getStepContent(step, formValues = null, changeFormValue = null, paymentValues = null, changePaymentValue = null) {
switch (step) {
case 0:
return <AddressForm addressValues={formValues} changeAddressValue={changeFormValue} />;
case 1:
return <PaymentForm PaymentForm={paymentValues} changePaymentForm={changePaymentValue}/>;
case 2:
return <Review />;
default:
throw new Error('Unknown step');
}
}
export default function Checkout(props) {
const classes = useStyles();
const [addressFormValues, setAddressFormValues] = React.useState({});
const [paymentFormValues, setPaymentFormValues] = React.useState({});
const [paymentFormNewValues, setPaymentFormNewValues] = React.useState({});
const [activeStep, setActiveStep] = React.useState(0);
if(paymentFormValues === true){
setPaymentFormNewValues('Cash')
}
if(paymentFormValues === false){
setPaymentFormNewValues('Online')
}
console.log('[paymentFormNewValues: ]', paymentFormNewValues)
console.log('[paymentFormValues: ]', paymentFormValues)
const handleNext = () => {
setActiveStep(activeStep + 1);
axios.post('/UserPortal/CartItems/checkout_details_check.php', {
customer_id: localStorage.getItem('Id'),
})
.then((response) => {
if(response.data === null)
{
axios.post('/UserPortal/CartItems/checkout_details.php', {
customer_id: localStorage.getItem('Id'),
firstname: addressFormValues.firstname,
lastname: addressFormValues.lastname,
address: addressFormValues.address,
city: addressFormValues.city,
state: addressFormValues.state
})
.then((response) => {
console.log(response.data);
})
}
else{
axios.post('/UserPortal/CartItems/checkout_details_update.php', {
customer_id: localStorage.getItem('Id'),
firstname: addressFormValues.firstname,
lastname: addressFormValues.lastname,
address: addressFormValues.address,
city: addressFormValues.city,
state: addressFormValues.state,
payment_method: paymentFormNewValues
})
.then((response) => {
console.log(response.data);
})
}
})
};
const handleBack = () => {
setActiveStep(activeStep - 1);
};
const changeAddressFormValue = (key, value) => {
let values = { ...addressFormValues };
values[key] = value;
setAddressFormValues(values);
};
const changePaymentFormValue = (key, value) => {
let values = { ...addressFormValues };
values[key] = value;
setPaymentFormValues(values);
};
return (
<React.Fragment>
<CssBaseline />
<AppBar position="absolute" color="default" className={classes.appBar}></AppBar>
<main className={classes.layout}>
<Paper className={classes.paper}>
<Typography component="h1" variant="h3" align="center">
Checkout
</Typography>
<Stepper activeStep={activeStep} className={classes.stepper}>
{steps.map((label) => (
<Step key={label}>
<StepLabel><Typography component="h1" variant="h5" align="center">
{label} </Typography></StepLabel>
</Step>
))}
</Stepper>
<React.Fragment>
{activeStep === steps.length ? (
<React.Fragment>
<Typography variant="h5" gutterBottom>
Thank you for your order.
</Typography>
<Typography variant="subtitle1">
Your order number is #2001539. We have emailed your order
confirmation, and will
send you an update when your order has shipped.
</Typography>
</React.Fragment>
) : (
<React.Fragment>
{activeStep === 0 ? getStepContent(activeStep, addressFormValues, changeAddressFormValue , paymentFormValues, changePaymentFormValue) : getStepContent(activeStep)}
{ <div className={classes.buttons}>
{ activeStep !== 0 && (
<Button variant="contained" style={{outline: 'none'}}
className={classes.button}
onClick={handleBack}
>
Back
</Button>
)}
<Button style={{outline: 'none'}}
variant="contained"
color="secondary"
onClick={handleNext}
className={classes.button}
>
{activeStep === steps.length - 1 ? 'Place order' : 'Next'}
</Button>
</div> }
</React.Fragment>
)}
</React.Fragment>
</Paper>
<Copyright />
</main>
</React.Fragment>
);
}
It seems you have checked the activeStep wrong.
Maybe the right code must be like the following:
<React.Fragment>
{activeStep !== 0 ?getStepContent(activeStep, addressFormValues, changeAddressFormValue , paymentFormValues, changePaymentFormValue) : getStepContent(activeStep)}
Do you ever consider using Context API?
React Context API
But also you can use create a checkboxValue with useState in Checkout.js and pass setCheckBoxValue to PaymentForm in getStepContent as prop. When checkbox checked trigger setCheckBoxValue and it will trigger parent component's state (Checkbox).

Using axios to make use of a service not working

I'm trying to use S3 service to upload an image and it's telling me that certain variables aren't defined when I have defined them. I have imported axios and all the other stuff that are required
import React, { useState } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import Card from "#material-ui/core/Card";
import TextField from "#material-ui/core/TextField";
import CreateIcon from "#material-ui/icons/Create";
import Box from "#material-ui/core/Box";
import CardMedia from "#material-ui/core/CardMedia";
import MuiAlert from "#material-ui/lab/Alert";
import Snackbar from "#material-ui/core/Snackbar";
import { withStyles } from "#material-ui/core/styles";
import { makeStyles } from "#material-ui/core/styles";
import Chip from "#material-ui/core/Chip";
import Avatar from "#material-ui/core/Avatar";
import Slider from "#material-ui/core/Slider";
import Typography from "#material-ui/core/Typography";
import InputAdornment from "#material-ui/core/InputAdornment";
import { connect } from "react-redux";
function mapStateToProps(state) {
return {};
}
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1),
marginLeft: theme.spacing(15),
},
},
input: {
display: "none",
},
}));
const useSliderStyles = makeStyles({
root: {
width: 250,
},
input: {
width: 100,
},
});
const UploadButton = () => {
const classes = useStyles();
return (
<div className={classes.root}>
<input
accept='image/*'
className={classes.input}
id='contained-button-file'
multiple
type='file'
/>
<label htmlFor='contained-button-file'>
<Button variant='contained' color='primary' component='span'>
Upload
</Button>
</label>
</div>
);
};
const StyledCard = withStyles({
root: { height: 600, width: 350 },
})(Card);
const PetitionForm = () => {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [open, setOpen] = useState(false);
const [petition, validPetition] = useState(false);
const [noTitle, titleError] = useState(false);
const [noDescription, descriptionError] = useState(false);
const [hashtag, setHashtag] = useState("");
const [arrayOfHashtags, addHashtag] = useState([]);
const [money, setMoney] = React.useState(500);
const slider = useSliderStyles();
const handleTitleChange = (event) => setTitle(event.target.value);
const handleDescriptionChange = (event) => setDescription(event.target.value);
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
};
const Alert = (props) => (
<MuiAlert elevation={6} variant='filled' {...props} />
);
const clearField = (event) => {
setOpen(true);
if (title.length > 0 && description.length > 0) {
validPetition(true);
setTitle("");
setDescription("");
addHashtag([]);
setHashtag("");
axios({
url: `call/s3/backend`,
method: "post",
data: {
images: imageArray.toByteArray(),
},
})
.then((res) => {
imageUrlArr = res.data;
axios({
url: `api/petition_posts`,
method: "post",
data: {
petition_post: {
title: title,
description: description,
hashtags: arrayOfHashtags.join(" "),
amount_donated: 0,
media: imageUrlArr,
goal: money,
card_type: "petition",
org_profile_id: 1,
},
},
})
.then((res) => {
console.log(res.data);
})
.catch((error) => console.log(error));
})
.catch((error) => console.log(error));
}
titleError(true ? title.length === 0 : false);
descriptionError(true ? description.length === 0 : false);
};
const handleDelete = (h) => () => {
addHashtag((arrayOfHashtags) =>
arrayOfHashtags.filter((hashtag) => hashtag !== h)
);
};
const handleHashtagChange = (event) => setHashtag(event.target.value);
const handleSliderChange = (event, newValue) => {
setMoney(newValue);
};
const handleInputChange = (event) => {
setMoney(event.target.value === "" ? "" : Number(event.target.value));
};
const newHashtag = () => {
if (arrayOfHashtags.length < 3) {
addHashtag((arrayOfHashtags) => arrayOfHashtags.concat(hashtag));
} else {
console.log("Too many hashtags");
}
};
const Hashtags = arrayOfHashtags.map((h) => (
<Chip
key={h.length}
size='small'
avatar={<Avatar>#</Avatar>}
label={h}
onDelete={handleDelete(h)}
/>
));
return (
<StyledCard>
<Box mt={1}>
<Grid container justify='center'>
<TextField
id='outlined-multiline-static'
multiline
rows={1}
variant='outlined'
placeholder='Title'
value={title}
onChange={handleTitleChange}
helperText={
open // only displays helper text if button has been clicked and fields haven't been filled
? !noTitle || petition
? ""
: "Can't be an empty field"
: ""
}
/>
</Grid>
</Box>
<Box mt={1}>
<Grid container justify='center'>
<CardMedia title='Petition'>
<UploadButton />
</CardMedia>
</Grid>
</Box>
<div className={slider.root}>
<Typography>Amount to raise</Typography>
<Box>
<Grid container justify='center'>
<Slider
min={500}
max={10000}
value={typeof money === "number" ? money : 0}
onChange={handleSliderChange}
aria-labelledby='input-slider'
/>
<TextField
className={slider.input}
value={money}
onChange={handleInputChange}
InputProps={{
startAdornment: (
<InputAdornment position='start'>$</InputAdornment>
),
}}
helperText={
money < 500 || money > 10000
? "Please enter a value between 500 and 10000"
: ""
}
/>
</Grid>
</Box>
</div>
<Box mt={1} mb={1}>
<Grid container justify='center'>
<TextField
size='small'
inputProps={{
style: { fontSize: 15 },
}}
id='outlined-multiline-static'
multiline
rows={1}
placeholder='Hashtags'
variant='outlined'
value={hashtag}
onChange={handleHashtagChange}
helperText={
arrayOfHashtags.length === 3
? "You reached the maximum amount of hashtags"
: ""
}
/>
<Button color='primary' onClick={newHashtag}>
Create!
</Button>
{arrayOfHashtags.length > 0 ? Hashtags : ""}
</Grid>
</Box>
<Box mt={1} justify='center'>
<Grid container justify='center'>
<TextField
size='small'
inputProps={{
style: { fontSize: 15 },
}}
id='outlined-multiline-static'
multiline
rows={5}
placeholder='Description'
variant='outlined'
value={description}
onChange={handleDescriptionChange}
helperText={
// only displays helper text if button has been clicked and fields haven't been filled
open
? !noDescription || petition
? ""
: "Can't be an empty field"
: ""
}
/>
</Grid>
</Box>
<Box mt={1}>
<Grid container justify='center'>
<Button onClick={clearField}>
<CreateIcon />
Create Petition!
</Button>
{open && petition && (
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert severity='success'>
You have successfully create a petition!
</Alert>
</Snackbar>
)}
{open && !petition && (
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert severity='error'>You're missing one or more fields</Alert>
</Snackbar>
)}
</Grid>
</Box>
</StyledCard>
);
};
export default connect(mapStateToProps)(PetitionForm);
This is the error I'm getting, someone mentioned something about the scope and I think that shouldn't matter when I'm trying to assign a value to a variable as far I as know
Line 109:19: 'imageArray' is not defined no-undef
Line 113:11: 'imageUrlArr' is not defined no-undef
Line 123:24: 'imageUrlArr' is not defined no-undef
Search for the keywords to learn more about each error.

Categories