Material UI Rotate Menu Icons Individually - reactjs

When I click on one button in a navigation bar, I want to only flip one icon instead of all of the icons in the navigation bar. Right now when I click on one button, all of the icons flip instead of just the one I clicked.
Here is a demo: https://codesandbox.io/s/festive-frost-53nny
Any help is greatly appreciated.

You are using one open state for all items. Create an array of open items like your anchor Array.
import "./styles.css";
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import clsx from "clsx";
import Button from "#material-ui/core/Button";
import { KeyboardArrowUp } from "#material-ui/icons";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import { menuItems } from "./menuItems";
const useStyles = makeStyles((theme) => ({
openX: {
transform: "scaleX(1)"
},
closeX: {
transform: "scaleX(-1)"
},
openY: {
transform: "scaleY(1)"
},
closeY: {
transform: "scaleY(-1)"
}
}));
export default function App() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (index, event) => {
setAnchorEl({ [index]: event.currentTarget });
setOpen((prevState) => {
const newState = [...prevState];
newState[index] = !prevState[index];
return newState;
});
};
const [open, setOpen] = React.useState(
new Array(Object.keys(menuItems).length).fill(false)
);
return (
<AppBar>
<Toolbar>
<div>
{Object.keys(menuItems).map((item, index) => (
<div key={index}>
<React.Fragment>
<Button onClick={(e) => handleClick(index, e)}>
{item}
<KeyboardArrowUp
className={clsx(
!open[index] && classes.closeY,
open[index] && classes.openY
)}
/>
</Button>
</React.Fragment>
</div>
))}
</div>
</Toolbar>
</AppBar>
);
}

Related

Passing useState setter function to child component for a click event but getting setState is not a function

I am using Material UI Drawer for sidebar and passing the setReplyDrawer to child ReplyReview so that I can close it from the child with a click event but getting error that setReplyDrawer is not a function.
replyDrawer state is used for opening and close a modal.
<Button onClick={()=>setReplyDrawer(false)}>Back</Button>
can anyone please let me know what am i doing wrong or how to correct it?
import { Avatar, Box, Button, Container, CssBaseline, Divider, Rating, Select, Stack, SwipeableDrawer } from '#mui/material'
import React, { useState } from 'react'
import { AiFillCaretDown } from "react-icons/ai";
import MouseOverPopover from '../common/MouseOverPopover';
import SelectOptions from '../common/SelectOptions';
import ReplyReview from './ReplyReview';
import { BsChat } from "react-icons/bs";
const ReviewList = ({name,comment,createTime,reply,star}) => {
const [action, setAction] = useState('')
const [showMore, setShowMore] = useState(false)
const [replyDrawer, setReplyDrawer] = useState(false)
return (
<div className='review-list-container'>
{/* some code here*/}
<div className='review-buttons'>
{/* Reply Button */}
<MouseOverPopover
mainContent={
<Button size='medium' onClick={()=>setReplyDrawer(true)} variant="outlined">
<BsChat style={{fontSize:'27px'}} />
</Button>}
popoverContent={`Add Tyson Hinton as a contact to start messaging`}
/>
<div>
<SwipeableDrawer
sx={{
width: drawerWidth,
zIndex: (theme) => theme.zIndex.drawer + 1,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: drawerWidth,
boxSizing: 'border-box',
},
padding:'20px'
}}
anchor={'right'}
open={replyDrawer}
onClose={()=>setReplyDrawer(false)}
onOpen={()=>setReplyDrawer(true)}
>
<ReplyReview name={name} replyDrawer={replyDrawer} setReplyDrawer={setReplyDrawer} />
</SwipeableDrawer>
</div>
</div>
</div>
)
}
export default ReviewList
ReplyReview.jsx
import { Button, Drawer, IconButton, InputBase, Paper, SwipeableDrawer } from '#mui/material'
import React, { useState } from 'react'
import { BsSearch } from "react-icons/bs";
import { AiOutlinePlusCircle } from "react-icons/ai";
import { BiArrowBack } from "react-icons/bi";
import './ReplyReview.css'
const ReplyReview = (name,replyDrawer,setReplyDrawer,first,setFirst) => {
const [buttonClicked, setButtonClicked] = useState(false)
const drawerWidth = 500;
console.log(replyDrawer,setReplyDrawer)
return (
<div className='reply-review-container'>
<header>
<Button onClick={()=>setReplyDrawer(false)}><BiArrowBack/></Button>
<strong>Start messaging {name.name}</strong>
</header>
</div>
)
}
export default ReplyReview
In ReplyReview the button is used to close the modal
You forgot to deconstruct the props
const ReplyReview = (name,replyDrawer,setReplyDrawer,first,setFirst) => {
to
const ReplyReview = ({name,replyDrawer,setReplyDrawer,first,setFirst}) => {
"name.name" should now be just "name"
Maybe you don't call that function in the Correct way
function ReplayReview(props){
// here props an object so you need to pull up any value from that object
return <Button onClick={()=>props.setReplyDrawer(false)}>Back</Button>
}
or
function ReplayReview({setReplayDrawer})=>{
//You can use object destructure in square bracket into function arguments. For Example: const obj={a:5,b:10}
// const {a,b}=obj or const a=obj.a ,const b=obj.b
return <Button onClick={()=>setReplayDrawer(false)}>back</Button>
}

API data not printing but successes with console.log

I'm trying to learn about APIs and trying to code a REACT app to go along with it. I am sure the issue is a minor one, but I can't seem to crack it.
The relevant code is pasted below, the API is fetched in index.js.
The contents of the API is printed to the console without issue but I can not seem to get it right when going through my list and event details.
I am new to coding so I would appreciate any feedback given.
App.js
import React, { useState, useEffect } from "react";
import { CssBaseline, Grid } from "#material-ui/core";
import { getEventsData } from "./api";
import Header from "./components/Header/Header";
import List from "./components/List/List";
import EventDetails from "./components/EventDetails/EventDetails";
const App = () => {
const [events, setEvents] = useState([]);
useEffect(() => {
getEventsData()
.then((data) => {
console.log(data);
console.log(Array.isArray(data))
setEvents(data);
})
}, []);
return (
<>
<CssBaseline />
<Header />
<List EventDetails={EventDetails} />
</>
)
}
export default App;
index.js
import axios from "axios";
const URL = 'https://api-football-v1.p.rapidapi.com/v3/fixtures'
const options = {
params: {date: '2022-02-12', league: '39', season: '2021'},
headers: {
'x-rapidapi-host': 'api-football-v1.p.rapidapi.com',
'x-rapidapi-key': xxxXXXxxxXXXxxx'
}
};
export const getEventsData = async () => {
try {
const { data } = await axios.get(URL, options);
// Kan det ha något med options att göra? https://stackoverflow.com/questions/68367352/multiple-url-variable-async-await-axios
return data;
} catch (error) {
}
};
List.jsx
import React, { useState } from "react";
import { CircularProgress, Grid, Typography, InputLabel, MenuItem, FormControl, Select, ButtonGroup, Button } from "#material-ui/core";
import EventDetails from "../EventDetails/EventDetails"
import useStyles from "./styles"
const List = ({ events }) => {
const classes = useStyles();
const [type, setType] = useState("premierleague");
return (
<div className={classes.container}>
<FormControl className={classes.formControl}>
<InputLabel>Sport</InputLabel>
<Select value={type} onChange={(e) => setType(e.target.value)}>
<MenuItem value="premierleague">Premier League</MenuItem>
<MenuItem value="formula1">Formula 1</MenuItem>
</Select>
{/*<ButtonGroup value={type} onClick={(e) => setType(e.target.value)}>
<Button value="premierleague">Premier League</Button>
<Button value="formula1">Formula 1</Button>
</ButtonGroup>*/}
</FormControl>
<Grid container spacing={3} className={classes.list}>
{events?.map((event, i) => (
<Grid item key={i} xs={12}>
<EventDetails event={event} />
</Grid>
))}
</Grid>
</div>
)
}
export default List;
EventDetails.jsx
import React from "react";
const EventDetails = ({ event }) => {
console.log(event)
return (
<h3>{event.league}</h3>
)
}
export default EventDetails;
You're not sending the events to List component.
Try changing in App.js:
return (
<>
<CssBaseline />
<Header />
<List events={events} />
</>
)

Materail UI x React: Autocomplete closes when an item tries to show Popover

I'm trying to have a customized Autocomplete and each item has a more icon when you hover a mouse pointer.
When you click the icon, it supposes to bring Popover up, but instead it closes the Autocomplete list.
The image above, I'm hovering a pointer on the icon, and when I clicked it, it closed the list.
App.js
import React, { useState } from "react";
import { TextField, Paper } from "#material-ui/core";
import { Autocomplete, useAutocomplete } from "#material-ui/lab";
import Item from "./Item";
const App = () => {
const [isOpen, setIsOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const countries = [
{ code: "US", label: "USA", phone: "1" },
{ code: "HU", label: "Hungary ", phone: "2" },
{ code: "IT", label: "Italy ", phone: "3" }
];
const openMultipleOptionSearch = (event) => {
setIsOpen(true);
setAnchorEl(event.currentTarget);
};
const {
getRootProps,
getInputLabelProps,
getInputProps,
getListboxProps,
getOptionProps,
groupedOptions
} = useAutocomplete({
id: "use-autocomplete-demo",
options: countries,
getOptionLabel: (option) => option.label,
disableCloseOnSelect: true
});
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
<Paper>
{groupedOptions.length > 0 &&
groupedOptions.map((option, index) => (
<Item key={index} text={option.label} onClose={() => {}} />
))}
{groupedOptions.length === 0 && <p>No match.</p>}
</Paper>
</div>
);
};
export default App;
Item.js
import React, { useState } from "react";
import MoreHoriz from "#material-ui/icons/MoreHoriz";
import { Box, Typography, Button } from "#material-ui/core";
import Popup from "./Popup";
const Item = (prop) => {
const [onMouseOver, setOnMouseOver] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const openDeleteItem = (event) => {
setAnchorEl(event.currentTarget);
setIsOpen(true);
};
const closeDeleteItem = () => {
setAnchorEl(null);
setIsOpen(false);
setOnMouseOver(false);
prop.onClose();
};
return (
<Box
onMouseOver={() => setOnMouseOver(true)}
onMouseOut={() => isOpen || setOnMouseOver(false)}
>
<div className="item-container">
<Typography variant="body1">{prop.text}</Typography>
</div>
<Button
style={{ display: onMouseOver ? "" : "none" }}
onClick={openDeleteItem}
>
<MoreHoriz />
</Button>
<Popup
isOpen={isOpen}
anchorEl={anchorEl}
onClose={() => closeDeleteItem()}
/>
</Box>
);
};
export default Item;
Popup.js
import React from 'react';
import { Popover } from '#material-ui/core';
const Popup = (prop) => {
return (
<Popover
id={prop.isOpen ? 'simple-popover' : undefined}
open={prop.isOpen}
anchorEl={prop.anchorEl}
onClose={() => prop.onClose()}
>
Popup content
</Popover>
);
};
export default Popup;
Does anyone know how to fix this?
I think your autocomplete popup is getting blurred when you click on more button, so it looses focus and closes the dropdown itself.
this might help you - OnBlur closing react component if clicked inside

Binding multipal row hang or crash web page using React with redux toolkit

https://codesandbox.io/s/large-data-array-with-redux-toolkit-forked-7tp63?file=/demo.tsx
In the given codesandbox example I am using react typescript with redux-toolkit
in codesandbox example. I am trying to bind countries with checkbox and textbox.
When I check on checkboxes it looks slow and textbox edit also feels very slow.
some time it breaks the page.
I am not sure what I am doing wrong.
You should make CountryItem it's own component and make it a pure component:
import React, { FC, useEffect } from "react";
import { createStyles, Theme, makeStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import { countryList } from "./dummyData";
import { Checkbox, Grid, TextField } from "#material-ui/core";
import { useAppDispatch, useAppSelector } from "./store/hooks";
import {
setCountries,
setCountrySelected,
setCountryValue
} from "./store/slice/geography-slice";
import { Country } from "./interface/country.modal";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: "100%",
backgroundColor: theme.palette.background.paper
}
})
);
const CountryItem: FC<{ country: Country }> = ({ country }) => {
console.log('render item',country.name)
const dispatch = useAppDispatch();
const handleCheckboxChange = (
event: React.ChangeEvent<HTMLInputElement>,
country: Country
) => {
const selectedCountry = { ...country, isSelected: event.target.checked };
dispatch(setCountrySelected(selectedCountry));
};
const handleTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const selectedCountry = { ...country, value: event.target.value };
dispatch(setCountryValue(selectedCountry));
};
return (
<ListItem button>
<Checkbox
checked={country?.isSelected ?? false}
onChange={(event) => {
handleCheckboxChange(event, country);
}}
/>
<ListItemText primary={country.name} />
<TextField value={country?.value ?? ""} onChange={handleTextChange} />
</ListItem>
);
};
const PureCountryItem = React.memo(CountryItem)
export default function SimpleList() {
const classes = useStyles();
const dispatch = useAppDispatch();
const { countries } = useAppSelector((state) => state.geography);
useEffect(() => {
dispatch(setCountries(countryList));
}, []);
return (
<div className={classes.root}>
<Grid container>
<Grid item xs={6}>
<List component="nav" aria-label="secondary mailbox folders">
{countries.map((country, index) => (
<PureCountryItem country={country} key={`CountryItem__${index}`} />
))}
</List>
</Grid>
</Grid>
</div>
);
}

makeStyle does not work with custom component

I am trying to make a simple navbar using material ui with a few buttons and a custom drop down menu component. When I try to style it using the makeStyle hook, the styling only applies to the material ui's buttons and heading but not the custom drop down component.
import React, { useContext } from "react";
import { makeStyles } from "#material-ui/core/styles";
import { AppBar, Toolbar, Typography } from "#material-ui/core";
import DropDown from "./DropDown";
import { Button } from "#material-ui/core";
import { AlgoContext } from "../AlgoContext";
const useStyles = makeStyles((theme) => ({
item: {
marginRight: theme.spacing(5),
},
}));
const MainHeader = () => {
const classes = useStyles();
const [algo, setAlgo] = useContext(AlgoContext);
return (
<div>
<AppBar elevation={0} position='static'>
<Toolbar>
<Typography variant='h6' className={classes.item}>
Pathfinding Visualiser
</Typography>
<Button variant='contained' className={classes.item}>
Visualise {algo.type}
</Button>
<DropDown className={classes.item}></DropDown>
<Button variant='contained' className={classes.item}>
Clear walls
</Button>
<Button variant='contained' className={classes.item}>
Clear path
</Button>
</Toolbar>
</AppBar>
</div>
);
};
export default MainHeader;
className is a default attribute of React element. You can not style your custom component by passing style object via className. Instead of that, you should pass it as a prop to DropDown component. Try this:
const MainHeader = () => {
const classes = useStyles();
const [algo, setAlgo] = useContext(AlgoContext);
return (
<DropDown itemStyle={classes.item}></DropDown>
);
};
export default MainHeader;
const DropDown = (props) => {
...
return (
<div className={props.itemStyle}>
...
</div>
)
}

Resources