need to create a new hidden div that shows after a onclick - javascript

I have a component that displays experiences. I want to create the option of showing a box ontop of my experiences with the different options they can do when they click an experience. I have different options for each experience.
This is my experiences
and I want to create a box like this with the corresponding images of each experience.
This is my experience component
import React, { memo, useCallback } from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '#material-ui/styles';
import Typography from '#material-ui/core/Typography';
import clsx from 'clsx';
import Popper from '#material-ui/core/Popper';
import gastronomia from 'assets/experiences/gastronomia.jpg';
import productos from 'assets/experiences/productos.jpg';
import giftcard from 'assets/experiences/giftcard.jpg';
import diversion from 'assets/experiences/diversion.jpg';
import deporte from 'assets/experiences/deporte.jpg';
import belleza from 'assets/experiences/belleza.jpg';
import gastronomiaExperiences from 'data/gastronomia';
import productosExperiences from 'data/productos';
import giftcardExperiences from 'data/giftcard';
import diversionExperiences from 'data/diversion';
import deporteExperiences from 'data/deporte';
import bellezaExperiences from 'data/belleza';
// Proptypes definitions to the component.
const propTypes = {
/** Custom root className. */
className: PropTypes.string,
};
// Default props definitions.
const defaultProps = {
className: null,
};
// Component's styles
const useStyles = makeStyles(theme => ({
root: {
display: 'block',
margin: '0 auto',
maxWidth: '50%',
[theme.breakpoints.down('md')]: {
maxWidth: '70%',
},
[theme.breakpoints.down('sm')]: {
maxWidth: '100%',
},
'& .experiences-column': {
display: 'inline-block',
verticalAlign: 'top',
textAlign: 'center',
'&.col1': {
width: '36.31%',
[theme.breakpoints.down('sm')]: {
width: 'initial',
},
},
'&.col2': {
width: '63.69%',
[theme.breakpoints.down('sm')]: {
width: 'initial',
},
},
'& .experience': {
padding: 2,
position: 'relative',
'& img': {
width: '100%',
display: 'block',
},
'& .experience-title': {
position: 'absolute',
bottom: 30,
left: 0,
right: 0,
textAlign: 'center',
},
},
},
},
}), { name: 'ExperiencesStyle' });
/**
* Component used to render a grid of experiences.
*
* #param {object} props - The component's props.
* #returns {object} React element.
*/
const Experiences = memo(
(props) => {
const { className } = props;
const classes = useStyles(props);
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = event => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? 'simple-popper' : undefined;
const experience = useCallback((img, title) => (
<div className="experience" aria-describedby={id} onClick={handleClick} onClick={() => setAnchorEl(!anchorEl)}>
<img
data-sizes="auto"
className="lazyload"
data-src={img}
alt={title}
/>
<div className="experience-title">
<Typography
color="textSecondary"
variant="subtitle2"
className="highlight highlight1"
display="inline"
>
{ title }
</Typography>
</div>
</div>
), []);
return (
<div className={clsx(classes.root, className)}>
<div className="experiences-column col1">
{experience(gastronomia, 'GASTRONOMÍA')}
{experience(giftcard, 'GIFT CARD')}
{experience(deporte, 'DEPORTE')}
</div>
<div className="experiences-column col2">
{experience(productos, 'PRODUCTOS')}
{experience(diversion, 'DIVERSIÓN')}
{experience(belleza, 'BELLEZA')}
</div>
<Popper id={id} open={open} anchorEl={anchorEl}>
<div className={classes.paper}>
<p>Array of images here depending on category </p>
</div>
</Popper>
</div>
);
},
);
// Component proptypes.
Experiences.propTypes = propTypes;
// Component default props.
Experiences.defaultProps = defaultProps;
export default Experiences;
I am complete lost on how to start this task. I am new to react

Ok, so
Delete experience from Experiences.
Add array of object - of your experiences
const iterateExperience = [
{img: gastronomia, title: 'GASTRONOMIA' },
{img: productos, title: 'PRODUCTOS' },
];
Later map it like this:
<div className={clsx(classes.root, className)}>
{iterateExperience.map((experience, index) => (
<div key = {index}>
<Experience img = {experience.img} title = {experience.title}/>
</div>
))}
</div>
Create Child Component - Experience:
const Experience = (props) => {
const [clicked, setClicked] = useState(true)
return (
<div onClick={() => setClicked(!clicked)}
className={clicked ? 'experience' : 'experience-clicked'}
>
<img
data-sizes="auto"
className="lazyload"
data-src={props.img}
alt={props.title}
/>
<div className="experience-title">
<Typography
color="textSecondary"
variant="subtitle2"
className="highlight highlight1"
display="inline"
>
{props.title}
</Typography>
</div>
</div>
);
}
export default Experience
And if You wan't render the different component based on click, just add in Experience (You can also apply another style - whatever you want)
const Experience = (props) => {
const [clicked, setClicked] = useState(true)
return (
<div onClick={() => setClicked(!clicked)} className='experience'>
{clicked ?
<div>
<img
data-sizes="auto"
className="lazyload"
data-src={props.img}
alt={props.title}
/>
<div className="experience-title">
<Typography
color="textSecondary"
variant="subtitle2"
className="highlight highlight1"
display="inline"
>
{props.title}
</Typography>
</div>
</div>
:
<p>Another Component</p>}
</div>
);
}

Related

Passing Data From One Component to Another React

Hey guys and gals i am trying to pass data from a text field in one component to another component. But the component that calls the text field is called in my Home.tsx file while i need the data from my Search.tsx file ( which also has where the text field is created ) to be sent to my searchResults.tsx file. Hope that makes sense
Here is my Home.tsx
import { ChakraProvider, Flex, Heading, IconButton, Input, Spacer, useColorMode, useDisclosure, VStack } from "#chakra-ui/react";
import Search from "../components/Search";
const Home = () => {
return(
<div>
<Flex>
<Search></Search>
</Flex>
</div>
)
}
export default Home
Here is my Search.tsx
import { TextField, IconButton} from "#material-ui/core"
import {Router, SearchOutlined } from '#material-ui/icons';
import { makeStyles } from "#material-ui/core/styles";
import "../App.css";
import { useState } from "react";
import { ChakraProvider,Box, color, position } from "#chakra-ui/react";
import { ReactComponent as Logo } from '../images/logo.svg';
const useStyles = makeStyles({
input: {
color: "blue"
}
})
const Search = () => {
const [searchTerm, setSearchTerm] = useState("");
const [displaySearchTerm, setDisplaySearchTerm] = useState("");
const useStyles = makeStyles((theme) => ({
input: {
color: "#fcba03",
},
}));
const classes = useStyles();
return(
<div>
<div className='searchCont' style={{alignItems: "center", justifyContent: 'center'}} >
<Logo style={{ height: 150, width: 300, position: "absolute", verticalAlign: "middle", marginTop: 200, left: '40%', backgroundColor: '#1a202c', borderRadius: 10 }} />
<TextField
inputProps={{color: classes.input}}
style={{width: 600, display: "flex", position: "absolute", verticalAlign: "middle", backgroundColor: "white", borderRadius: 5, alignItems: "center", justifyContent: 'center', marginTop: 400, marginBottom: 50, left: '32%', marginRight: 200}}
margin="normal"
className="textField"
fullWidth
id="standard-bare"
variant="outlined"
label="Search for Domains..."
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
endAdornment: (
<IconButton onClick={() =>{searchTerm}}>
<SearchOutlined />
</IconButton>
),
}}
/>
</div>
</div>
)
}
export default Search
Here is my searchResults.tsx
import { Flex } from "#chakra-ui/react";
import React from "react";
import {searchTerm} from "../components/Search";
const SearchResults = () => {
return(
<div>
<Flex>
<Search data={searchTerm} /> //error here
</Flex>
</div>
)
}
export default SearchResults;
use Redux as your state management system for this, this will help you read and write states across application without worrying.
You can also try react Context if its a one off issue and you dont wana invest into the redux system and its boiler plate code, but if you face this issue more frequently as your project grows complex use redux.

How to add search functionality to the SearchAppBar in Material UI?

So I have a basic react app that allows the user to search for and display movies from the Movie Database API.
Heres the problem: I want to link my custom React Function SearchMovies() to a material ui component, specifically the Material Ui SearchAppBar. However, I am having trouble adding this function to the Material Ui SearchAppBar.
First, my custom code that features the SearchMovies() function. The code is below:
import React, { useState } from "react";
import MovieCard from "./movieCard";
export default function SearchMovies() {
//states = input query, movies
const [query, setQuery] = useState("");
const [movies, setMovies] = useState([]);
const searchMovies = async (e) => {
e.preventDefault();
const url = `https://api.themoviedb.org/3/search/movie?api_key=a2657ca16cc801deb9a65e9f7f9e3d4f&language=en-US&query=${query}&page=1&include_adult=false`;
try {
const res = await fetch(url);
const data = await res.json();
//console.log(data.results);
setMovies(data.results);
} catch (err) {
console.error(err);
}
};
return (
<>
<form className="form" onSubmit={searchMovies}>
<label className="label" htmlFor="query">
Movie Name
</label>
<input
type="text"
className="input"
name="query"
placeholder="i.e. Star Wars"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button className="button" type="Submit">
Search
</button>
</form>
<div className="card-list">
{movies
.filter((movie) => movie.poster_path)
.map((movie) => (
<MovieCard movie={movie} key={movie.id} />
))}
</div>
</>
);
}
Second, here is the code for the Material UI SearchAppBar:
import React from "react";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import IconButton from "#material-ui/core/IconButton";
import Typography from "#material-ui/core/Typography";
import InputBase from "#material-ui/core/InputBase";
import { fade, makeStyles } from "#material-ui/core/styles";
import MenuIcon from "#material-ui/icons/Menu";
import SearchIcon from "#material-ui/icons/Search";
import searchMovies from "./searchMovies";
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
display: "none",
[theme.breakpoints.up("sm")]: {
display: "block",
},
},
search: {
position: "relative",
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
"&:hover": {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(1),
width: "auto",
},
},
searchIcon: {
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
inputRoot: {
color: "inherit",
},
inputInput: {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: "12ch",
"&:focus": {
width: "20ch",
},
},
},
}));
export default function SearchAppBar() {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="open drawer"
>
<MenuIcon />
</IconButton>
<Typography className={classes.title} variant="h5" noWrap>
MovieZone
</Typography>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
//value={query}
//onChange={(e) => setQuery(e.target.value)}
onSubmit={searchMovies}
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ "aria-label": "search" }}
/>
</div>
</Toolbar>
</AppBar>
</div>
);
}
So how can I inject the SearchMovies() function into the Material UI SearchAppBar, so that the user can use the Material UI component to call the SearchMovies() function and display the movie results?
You can take the reference from an angular material table example but you have mat cards its just the matter of filtering the dataSource what you give and recall that method or just change the arrayList by constructing filter arraylist or something which you provide again to the UI component
Here is the link
https://stackblitz.com/angular/kgboooaejyn?embed=1&file=src/app/table-filtering-example.ts
you need to focus on this function
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
You won't have a Datasource here but you can filter contents from ArrayList
Hope this idea works...!!

Switch function in react component not working as logic would suggest

So my react code does not execute as logic would suggest:
I am trying to create a page that resembles an email app.
I have components in React which I import into my message(main) component (e.g. Inbox component, send mail component etc). I have a useState, which is set to 'inbox' as default and a switch statement which checks which value is in the state and returns the component matching that case. I also have a onclick function which changes the state value based on which email tab you want to view (e.g. Inbox or trash etc). When the state changes the component should run the switch statement in my page render and display the correct component in the main component, which is does most of the time.
My problem comes when I keep switching between the tabs which have components it at some point goes to the default of the switch statement which does not make sense because either of the values should have passed.
Please help
import React, {useState} from 'react';
// Components to be displayed
import Inbox from './Inbox';
import SendMessage from './SendMessage';
// Other components on the page which you can ignore
import Navbar from '../Layout/Navbar';
import Copyright from '../Layout/Copyright';
import SendMail from './SendMail';
//Material ui for styling
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Divider from '#material-ui/core/Divider';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
import InboxIcon from '#material-ui/icons/MoveToInbox';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import MailIcon from '#material-ui/icons/Mail';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import Link from '#material-ui/core/Link';
import ControlPointIcon from '#material-ui/icons/ControlPoint';
const drawerWidth = 240;
const list = [
{id: 1, title: 'Singing lessons', message: 'We are practicing at 5:00'},
{id: 2, title: 'Meeting', message: 'Hi Guys, we can meet during lunch at Nandos'},
{id: 3, title: 'New Product release', message: 'Hi guys, we are encouraging everyone to buy in on the new product'}
]
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex'
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
zIndex: '0'
}
},
appBar: {
[theme.breakpoints.up('sm')]: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
},
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none',
},
},
// necessary for content to be below app bar
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
},
content: {
flexGrow: 1,
padding: theme.spacing(3)
},
listItemStyle: {
marginTop: '50px'
},
newMessage: {
fontSize: '40px',
backgroundColor: '#64b5f6',
borderRadius: '50px',
color: '#fff',
position: 'absolute',
bottom: '50px',
right: '50px',
'&:hover': {
backgroundColor: '#1976d2'
}
}
}));
// Main component
const Message = (props) => {
const { window } = props;
const classes = useStyles();
const theme = useTheme();
const [mobileOpen, setMobileOpen] = useState(false);
const [tabTracker, setTabTracker] = useState('Inbox');
const renderSwitch = (param) => {
switch(param) {
case 'Inbox':
return <Inbox />;
case 'Send email':
return <SendMail />;
case 'Drafts':
return 'Draft';
case 'Trash':
return 'Trash';
case 'Spam':
return 'Spam';
case 'newMessage':
return <SendMessage />;
default:
return 'foo';
}
}
const tabControl = (e) => {
setTabTracker(e.target.firstChild.data);
//renderSwitch(tabTracker);
}
const newMsg = (e) => {
setTabTracker(e.target.attributes[5].value);
//renderSwitch(tabTracker);
}
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List className={classes.listItemStyle}>
{['Inbox', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text} onClick={tabControl}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['Trash', 'Spam'].map((text, index) => (
<ListItem button key={text} onClick={tabControl}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
const container = window !== undefined ? () => window().document.body : undefined;
return (
<React.Fragment>
<span>
<Navbar />
</span>
<div className={classes.root}>
<CssBaseline />
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer
container={container}
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
{renderSwitch(tabTracker)}
<Link href="#">
<ControlPointIcon value="newMessage" primary="newMessage" className= {classes.newMessage} onClick={newMsg} />
</Link>
</main>
</div>
<Copyright />
</React.Fragment>
);
}
// ResponsiveDrawer.propTypes = {
// /**
// * Injected by the documentation to work in an iframe.
// * You won't need it on your project.
// */
// window: PropTypes.func,
// };
export default Message;
e.target gives you the element on which click was actually triggered, so when you have multiple elements as children within ListItem, its possible that you would have clicked on ListItemIcon and hence e.target is listItemIcon which was not your intention as it will give you an incorrect value for e.target.firstChild.data
So you could either use e.currentTarget which will give you the element on which the onClick listener is attached or you could simply pass on the information to tabControl by using an inline arrow function
const tabControl = (value) => {
setTabTracker(value);
}
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List className={classes.listItemStyle}>
{['Inbox', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text} onClick={() => tabControl(text)}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['Trash', 'Spam'].map((text, index) => (
<ListItem button key={text} onClick={() => tabControl(text)}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);

when I use "useCallback " I get a TypeError error

I have const experience that creates 6 experiences with there popover. I am supposed to add useCallback to it but when I go I get and error.
This is my component experience
import React, { memo, useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '#material-ui/styles';
import Typography from '#material-ui/core/Typography';
import clsx from 'clsx';
import Button from '#material-ui/core/Button';
import Popover from '#material-ui/core/Popover';
import gastronomia from 'assets/experiences/gastronomia.jpg';
import productos from 'assets/experiences/productos.jpg';
import giftcard from 'assets/experiences/giftcard.jpg';
import diversion from 'assets/experiences/diversion.jpg';
import deporte from 'assets/experiences/deporte.jpg';
import belleza from 'assets/experiences/belleza.jpg';
import gastronomiaExperiences from 'data/gastronomia';
import productosExperiences from 'data/productos';
import giftcardExperiences from 'data/giftcard';
import diversionExperiences from 'data/diversion';
import deporteExperiences from 'data/deporte';
import bellezaExperiences from 'data/belleza';
// Proptypes definitions to the component.
const propTypes = {
/** Custom root className. */
className: PropTypes.string,
};
// Default props definitions.
const defaultProps = {
className: null,
};
// Component's styles
const useStyles = makeStyles(theme => ({
root: {
display: 'block',
margin: '0 auto',
maxWidth: '50%',
[theme.breakpoints.down('md')]: {
maxWidth: '70%',
},
[theme.breakpoints.down('sm')]: {
maxWidth: '100%',
},
'& .experiences-column': {
display: 'inline-block',
verticalAlign: 'top',
textAlign: 'center',
'&.col1': {
width: '36.31%',
[theme.breakpoints.down('sm')]: {
width: 'initial',
},
},
'&.col2': {
width: '63.69%',
[theme.breakpoints.down('sm')]: {
width: 'initial',
},
},
'& .experience': {
padding: 2,
position: 'relative',
'& img': {
width: '100%',
display: 'block',
},
'& .experience-title': {
position: 'absolute',
bottom: 30,
left: 0,
right: 0,
textAlign: 'center',
},
},
},
},
paper: {
width: '50%',
left: '25% !important',
height: '280px',
'& img': {
width: '100px',
padding: '0 10px 0 10px',
},
},
gastronomia: {
backgroundColor: 'rgba(0,185,208,0.9)',
},
giftcard: {
backgroundColor: 'rgba(221,165,174,0.9)',
},
deporte: {
backgroundColor: 'rgba(189,143,205,0.9)',
},
productos: {
backgroundColor: 'rgba(221,165,174,0.9)',
},
diversion: {
backgroundColor: 'rgba(255,176,10,0.9)',
},
belleza: {
backgroundColor: 'rgba(229,166,187,0.9)',
},
'#media screen and (max-width: 1024px)': {
paper: {
width: '70%',
left: '15% !important',
},
},
'#media screen and (max-width: 480px)': {
paper: {
width: '100%',
left: '0% !important',
height: '350px',
},
},
}), { name: 'ExperiencesStyle' });
*/const Experiences = memo(
(props) => {
const { className } = props;
const classes = useStyles(props);
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
// const open = Boolean(anchorEl);
const experience = useCallback((img, title, id, popoverCategory, anchorEl, classes, handleClick) => (
<div
className="experience"
aria-describedby={id}
id={id}
onClick={handleClick}
onKeyDown={handleClick}
role="button"
tabIndex="0"
>
<img
data-sizes="auto"
className="lazyload"
data-src={img}
alt={title}
/>
<div className="experience-title">
<Typography
color="textSecondary"
variant="subtitle2"
className="highlight highlight1"
display="inline"
>
{ title }
</Typography>
</div>
<Popover
id={id}
open={anchorEl && anchorEl.id === id}
anchorEl={anchorEl}
onClose={handleClose}
classes={{paper: clsx(classes.paper, classes[id])}}
>
<div>
<Button onClickAway={handleClose}>x</Button>
<div>
{
popoverCategory.map(url => (
<img
key={url}
data-sizes="auto"
className="lazyload"
src={url}
alt={title}
/>
))
}
</div>
</div>
</Popover>
</div>
), []);
return (
<div className={clsx(classes.root, className)}>
<div className="experiences-column col1">
{experience(gastronomia, 'GASTRONOMÍA', 'gastronomia', gastronomiaExperiences)}
{experience(giftcard, 'GIFT CARD', 'giftcard', giftcardExperiences)}
{experience(deporte, 'DEPORTE', 'deporte', deporteExperiences)}
</div>
<div className="experiences-column col2">
{experience(productos, 'PRODUCTOS', 'productos', productosExperiences)}
{experience(diversion, 'DIVERSIÓN', 'diversion', diversionExperiences)}
{experience(belleza, 'BELLEZA', 'belleza', bellezaExperiences)}
</div>
</div>
);
},
);
and the error is:
TypeError: Cannot read property 'paper' of undefined
referring to this line
classes={{paper: clsx(classes.paper, classes[id])}}
where I add the classes to the paper class of the popover.
I am not used to useCallback and new to react so I am lost.
const experience = useCallback((img, title, id, popoverCategory, anchorEl, classes, handleClick) => (
The function you have created expects 7 things to be passed into it. But when you use it, you only pass in 4:
experience(gastronomia, 'GASTRONOMÍA', 'gastronomia', gastronomiaExperiences)
So the remaining 3 are all undefined inside the function. the anchorEl, classes, and handleClick variables defined at the top of your component are not visible inside experience, because those variable are being "shadowed".
So you could stop shadowing the variables by simply removing the last 3 arguments from your function definition:
const experience = useCallback((img, title, id, popoverCategory) => (
However, i do have to express that useCallback doesn't seem to be doing anything for you. The benefit of useCallback is that you can have the experience variable be the same reference from one render to the next, but that doesn't seem to be a feature that you need. Your component never tries to compare references of experience, nor is experience passed to any other component where it might be checked in a shouldComponentUpdate or React.memo.
So i would recommend deleting the useCallback entirely:
const experience = (image, title, id, popoverCategory) => (

I have 6 popovers that open at the same time but want them to open one by one

I have a component that creates a div with 6 experiences and each experience has a popover. But when I click the experience to open its popover I get all 6 popovers open. How do I give a unique id to each popover.
This is my experience component
import React, { memo, useCallback } from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '#material-ui/styles';
import Typography from '#material-ui/core/Typography';
import clsx from 'clsx';
import Popper from '#material-ui/core/Popper';
import gastronomia from 'assets/experiences/gastronomia.jpg';
import productos from 'assets/experiences/productos.jpg';
import giftcard from 'assets/experiences/giftcard.jpg';
import diversion from 'assets/experiences/diversion.jpg';
import deporte from 'assets/experiences/deporte.jpg';
import belleza from 'assets/experiences/belleza.jpg';
import gastronomiaExperiences from 'data/gastronomia';
import productosExperiences from 'data/productos';
import giftcardExperiences from 'data/giftcard';
import diversionExperiences from 'data/diversion';
import deporteExperiences from 'data/deporte';
import bellezaExperiences from 'data/belleza';
// Proptypes definitions to the component.
const propTypes = {
/** Custom root className. */
className: PropTypes.string,
};
// Default props definitions.
const defaultProps = {
className: null,
};
// Component's styles
const useStyles = makeStyles(theme => ({
root: {
display: 'block',
margin: '0 auto',
maxWidth: '50%',
[theme.breakpoints.down('md')]: {
maxWidth: '70%',
},
[theme.breakpoints.down('sm')]: {
maxWidth: '100%',
},
'& .experiences-column': {
display: 'inline-block',
verticalAlign: 'top',
textAlign: 'center',
'&.col1': {
width: '36.31%',
[theme.breakpoints.down('sm')]: {
width: 'initial',
},
},
'&.col2': {
width: '63.69%',
[theme.breakpoints.down('sm')]: {
width: 'initial',
},
},
'& .experience': {
padding: 2,
position: 'relative',
'& img': {
width: '100%',
display: 'block',
},
'& .experience-title': {
position: 'absolute',
bottom: 30,
left: 0,
right: 0,
textAlign: 'center',
},
},
},
},
}), { name: 'ExperiencesStyle' });
/**
* Component used to render a grid of experiences.
*
* #param {object} props - The component's props.
* #returns {object} React element.
*/
const Experiences = memo(
(props) => {
const { className } = props;
const classes = useStyles(props);
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = event => {
setAnchorEl(event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? 'simple-popper' : undefined;
const experience = (img, title, category, id, popoverCategory) => (
<div className="experience" aria-describedby={id} id={id} onClick={handleClick} >
<img
data-sizes="auto"
className="lazyload"
data-src={img}
alt={title}
/>
<div className="experience-title">
<Typography
color="textSecondary"
variant="subtitle2"
className="highlight highlight1"
display="inline"
>
{ title }
</Typography>
</div>
<Popper id={id} open={anchorEl && anchorEl.id === id} >
<div className={classes.paper}>
<img
data-sizes="auto"
className="lazyload"
data-src={popoverCategory}
alt="Puntospoint"
/>
</div>
</Popper>
</div>
);
return (
<div className={clsx(classes.root, className)}>
<div className="experiences-column col1">
{experience(gastronomia, 'GASTRONOMÍA', 'gastronomia', 'gastronomia', open, anchorEl, gastronomiaExperiences)}
{experience(giftcard, 'GIFT CARD', 'giftcard','giftcard', open, anchorEl, giftcardExperiences)}
{experience(deporte, 'DEPORTE', 'deporte', 'deporte', open, anchorEl, deporteExperiences)}
</div>
<div className="experiences-column col2">
{experience(productos, 'PRODUCTOS', 'productos', 'productos', open, anchorEl, productosExperiences)}
{experience(diversion, 'DIVERSIÓN', 'diversion', 'diversion', open, anchorEl, diversionExperiences)}
{experience(belleza, 'BELLEZA', 'belleza', 'belleza', open, anchorEl, bellezaExperiences)}
</div>
</div>
);
},
);
// Component proptypes.
Experiences.propTypes = propTypes;
// Component default props.
Experiences.defaultProps = defaultProps;
export default Experiences;
I am new to react and material ui so I am a bit lost here.
Give each experience its own id and remove the anchorEl parameter by changing this:
<div className={clsx(classes.root, className)}>
<div className="experiences-column col1">
{experience(gastronomia, 'GASTRONOMÍA', 'gastronomia', id, open, anchorEl, gastronomiaExperiences)}
{experience(giftcard, 'GIFT CARD', 'giftcard', id, open, anchorEl, giftcardExperiences)}
{experience(deporte, 'DEPORTE', 'deporte', id, open, anchorEl, deporteExperiences)}
</div>
<div className="experiences-column col2">
{experience(productos, 'PRODUCTOS', 'productos', id, open, anchorEl, productosExperiences)}
{experience(diversion, 'DIVERSIÓN', 'diversion', id, open, anchorEl, diversionExperiences)}
{experience(belleza, 'BELLEZA', 'belleza', id, open, anchorEl, bellezaExperiences)}
</div>
</div>
);
to this
<div className={clsx(classes.root, className)}>
<div className="experiences-column col1">
{experience(gastronomia, 'GASTRONOMÍA', 'gastronomia', 'gastronomia', open, gastronomiaExperiences)}
{experience(giftcard, 'GIFT CARD', 'giftcard','giftcard', open, giftcardExperiences)}
{experience(deporte, 'DEPORTE', 'deporte', 'deporte', open, deporteExperiences)}
</div>
<div className="experiences-column col2">
{experience(productos, 'PRODUCTOS', 'productos', 'productos', open, productosExperiences)}
{experience(diversion, 'DIVERSIÓN', 'diversion', 'diversion', open, diversionExperiences)}
{experience(belleza, 'BELLEZA', 'belleza', 'belleza', open, bellezaExperiences)}
</div>
</div>
);
That will give each experience own id. Then all you have to do is check whether the id of the anchorEl is the same as id you gave the experience.
Change this:
<Popper id={id} open={open} anchorEl={anchorEl}>
<div className={classes.paper}>
<LogoSlider logos={popoverCategory} />
</div>
</Popper>
to this
<Popper id={id} open={anchorEl && anchorEl.id === id}>
<div className={classes.paper}>
<LogoSlider logos={popoverCategory} />
</div>
</Popper>
also remove the useCallback hook, you don't need that.
Change this:
const experience = useCallback((img, title, category, id, open, anchorEl, popoverCategory) => (
to this:
const experience =(img, title, category, id, popoverCategory) => (
make 5 promises and reduce them into one big promise and use this promise Like this
const promisesArray = [Prmise1,Prmise2,Prmise3,Prmise4,Prmise5];
const allInOnePromise = promisesArray.reduce( (previousPromise, nextPromise) => {
return previousPromise.then(() => {
return nextPromise;
});
}, Promise.resolve());
allInOnePromise.then(_=>{});

Categories

Resources