I'm stuck on writing out logic that will show more comments if user clicks show more comments.
How would i go about performing this logic that filters based on the following: initially there are 2 comments that show(out of 7 total comments). I want to break this down as user keeps clicking on show more. Should show 5 more comments, 3 more comments, 1 more comment, until there is no more comments.
I'm not too sure on what im doing.
CommentList.tsx
import React, { Fragment, useState } from "react";
import Grid from "#material-ui/core/Grid";
import List from "#material-ui/core/List";
import Typography from "#material-ui/core/Typography";
import CommentItem from "./../commentItem/CommentItem";
import moment from "moment";
import OurLink from "../../../common/OurLink";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import OurModal from "../../../common/OurModal";
const ourStyle = {
backgroundColor: "#FAFAFA",
border: "1px solid #f2f2f2",
borderRadius: "4px",
padding: "15px 20px",
margin: "15px",
};
function CommentList(props: any) {
const [showMore, setShowMore] = useState<Number>(2);
const [openModal, setOpenModal] = useState(false);
const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
const lastIndexOfComments = props.comments.length - 1;
const startIndex = 0;
console.log(lastIndexOfComments);
const showComments = (e) => {
e.preventDefault();
const subtract = props.comments.length - 2 ;
console.log("testing", subtract);
setShowMore(subtract);
// setShowLessFlag(true);
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
.....
const showMoreComments = () => {
return props.comments
.slice(startIndex, showMore)
.sort((a, b) => a.id - b.id)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ cursor: "pointer", fontSize: "12px", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
</span>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
));
};
const ourComments = props.comments;
console.log("before comments", ourComments);
console.log("fsfsfsftestingcomments", ourComments.slice(0, showMore));
return (
<Grid>
<Fragment>
<div style={{ margin: "30px 0px" }}>
<OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
View More Comments
</OurSecondaryButton>
</div>
</Fragment>
{showLessFlag === true ? (
// will show most recent comments below
showMoreComments()
) : (
<Fragment>
{/* filter based on first comment */}
{props.comments
.filter((item, i) => item)
.sort((a, b) => b.id - a.id)
.slice(startIndex, showMore)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ fontSize: "12px", cursor: "pointer", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</span>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
))}
</Fragment>
)}
</Grid>
);
}
// prevents un-necesary re renders
export default React.memo(CommentList);
If you have all comments already fetched, you could simply set a limit and only show the comments with an index lower than the limit.
Check this out: https://stackoverflow.com/a/44071932/10955418
Related
I'm trying to pass the control prop to my custom component that comes from react-hook-form but it tells that the control is undefined
I did the same thing written in document. I used FormProvider like this:
<FormProvider {...methods}>
<form
style={{
height: "auto",
width: "90%",
padding: "2em",
paddingTop: "0",
textAlign: "center",
backgroundColor: "white",
borderRadius: "10px",
}}
onSubmit={methods.handleSubmit((data) => console.log(data))}
>
<IconButton
color="error"
onClick={() => setRecordControl({ stat: "" })}
>
<Cancel fontSize="large" />
</IconButton>
<h3 style={{ marginBottom: "10px", textAlign: "center" }}>
saving records
</h3>
{filtered.ListColumnInfo.map(
(detail) =>
detail.VisibleColumn && (
<div key={uuidV4()}>
<CustomTextBoxComponent
filtered={detail}
/>
</div>
)
)}
<Button
variant="contained"
fullWidth
color="success"
sx={{ mt: "1em" }}
type="submit"
>
save
</Button>
</form>
</FormProvider>
and I tried to retrieve the control inside of my CustomTextBoxComponent like this:
const CustomTextBoxComponent = ({ filtered }) => {
const { control } = useFormContext();
return (
<Controller
name="input"
render={({ field }) => (
<TextField
fullWidth
placeholder={
filtered.ElsRecord.PersianName !== ""
? filtered.ElsRecord.PersianName
: filtered.ElsRecord.Name
}
sx={{ marginBlock: "1em" }}
{...control}
{...field}
/>
)}
defaultValue=""
/>
);
};
it didn't work. always says the method is null
the picture of Error
how can I solve it?
I want to map my products in mui and put each one in Grow, but with this error Warning: Failed prop type: Invalid prop children of type array supplied to ForwardRef(Grow), expect a single ReactElement . I will be
what should i do
if (enter) {
return (
<Box
sx={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, 250px)",
gap: "10px",
alignItems: "center",
justifyContent: "space-evenly",
padding: "0 50px",
height: "100%",
}}
>
{searchState.length ? (
<>
<Grow in={true}>
{searchState.map((item) => (
<Product key={item.id} productData={item} />
))}
</Grow>
<Grow
in={true}
style={{ transformOrigin: "0 0 0" }}
{...(true ? { timeout: 1000 } : {})}
>
{searchState.map((item) => (
<Product key={item.id} productData={item} />
))}
</Grow>
</>
) : (
<Box sx={{stylesNone}>No products found</Box>
)}
</Box>
);
}
You can wrap your mapped components with a div or fragment (<></>)
<Grow in={true}>
<>
{searchState.map((item) => (
<Product key={item.id} productData={item} />
))}
</>
</Grow>
it gives me this error
App.js:14 Uncaught TypeError: Cannot destructure property 'theme' of '(0 , react__WEBPACK_IMPORTED_MODULE_8__.useContext)(...)' as it is undefined
[IN CART.JS I CAN EASILY TAKE MY ADDTOCART STATE, BUT IN APP.JS IT DOESN'T LET ME TAKE THE "THEME" STATE AND I DON'T KNOW WHAT WILL BE THE REASON]
[CARTCONTEXT.JS]
import React, { createContext, useState } from "react";
export const CarritoContext = createContext();
export default function CartContext({ children }) {
const [addToCarrito, setAddToCarrito] = useState([]);
const [theme, setTheme] = useState("light");
async function addItem(item, quantity) {
const foundProdIndex = await addToCarrito.findIndex(
(product) => product.item.id === item.id
);
if (foundProdIndex === -1) {
setAddToCarrito([...addToCarrito, { item, quantity }]);
}
if (quantity === 0) {
setAddToCarrito([...addToCarrito]);
}
}
function clear() {
setAddToCarrito([]);
}
function removeItem(itemId) {
const newItems = addToCarrito.filter((item) => item.item.id !== itemId);
setAddToCarrito(newItems);
}
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
};
return (
<>
<CarritoContext.Provider
value={{
addToCarrito,
setAddToCarrito,
clear,
addItem,
removeItem,
theme,
toggleTheme,
}}
>
{children}
</CarritoContext.Provider>
</>
);
}
[APP.JS]
import "./App.scss";
import ItemDetailContainer from "./components/ItemDetailContainer/ItemDetailContainer";
import ItemListContainer from "./components/ItemListContainer/ItemListContainer";
import Navbar from "./components/Navbar/Navbar";
import { Routes, Route } from "react-router-dom";
import ItemList from "./components/ItemList/ItemList";
import NotFound404 from "./components/NotFound404/NotFound404";
import Cart from "./components/Cart/Cart";
import CartContext, { CarritoContext } from "./context/CartContext";
import React, { useContext } from "react";
function App() {
let { theme } = useContext(CarritoContext);
return (
<>
<CartContext>
<div className="App" id={theme}>
<Navbar />
<Routes>
<Route path="/carvel-ecommerce" element={<ItemListContainer />} />
<Route
path="/carvel-ecommerce/category/:id"
element={<ItemList />}
/>
<Route
path="/carvel-ecommerce/item/:id"
element={<ItemDetailContainer />}
/>
<Route path="/carvel-ecommerce/cart" element={<Cart />} />
<Route path="*" element={<NotFound404 />} />
</Routes>
</div>
</CartContext>
</>
);
}
export default App;
[CART.JS] (HERE THE CONTEXT WORKS FINE]
import React, { useContext } from "react";
import { Link } from "react-router-dom";
import { CarritoContext } from "../../context/CartContext";
import "./Cart.scss";
import { BsTrash } from "react-icons/bs";
import { MdOutlineCleaningServices } from "react-icons/md";
import { motion } from "framer-motion";
import { v4 as uuidv4 } from "uuid";
import { ReactComponent as Womens } from "../../assets/women-coat-svgrepo-com.svg";
import { ReactComponent as Hats } from "../../assets/accesory-hats-svgrepo-com.svg";
import { ReactComponent as Glasses } from "../../assets/glasses-svgrepo-com.svg";
import { ReactComponent as Jackets } from "../../assets/jacket-svgrepo-com.svg";
const Cart = () => {
let { addToCarrito } = useContext(CarritoContext);
let { clear } = useContext(CarritoContext);
let { removeItem } = useContext(CarritoContext);
const variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
const totalPrice = addToCarrito
.map((product) => {
return product.item.price * product.quantity;
})
.reduce((item, qty) => item + qty, 0);
const pickData = addToCarrito.map((producto) => {
return (
<p key={uuidv4()}>
<small>
{producto.item.name}({producto.quantity}) :
<strong> ${producto.item.price * producto.quantity}</strong>
</small>
</p>
);
});
return (
<>
{addToCarrito.length > 0 ? (
addToCarrito.map((producto) => {
return (
<motion.div
initial="hidden"
animate="visible"
variants={variants}
key={uuidv4()}
>
<motion.div
initial="hidden"
animate="visible"
variants={variants}
className="container"
>
<motion.section
initial="hidden"
animate="visible"
variants={variants}
id="cart"
>
<motion.article
initial="hidden"
animate="visible"
variants={variants}
className="product"
>
<header>
<img
src={producto.item.imageUrl}
alt=""
style={{
backgroundColor: "white",
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
</header>
<div className="content">
<h1>
{producto.item.name}
<BsTrash
onClick={() => removeItem(producto.item.id)}
className="basurita"
/>
</h1>
{producto.item.category} | {producto.item.color}
<h5 style={{ marginTop: "10px" }}>
<span>
<small className="initial-price">
<span
style={{
backgroundColor: "#00640a",
height: "120px",
}}
>
</span>{" "}
Initial price{" "}
</small>
</span>
</h5>
<h5>
<span>
<small className="final-price">
<span
style={{
height: "120px",
backgroundColor: "yellow",
}}
>
</span>
<span
style={{
height: "120px",
backgroundColor: "black",
}}
>
</span>{" "}
Final price{" "}
</small>
</span>
</h5>
</div>
<footer className="content">
<span className="qt">
<strong>Qty: </strong> {producto.quantity}
</span>
<h2 className="full-price">
${producto.item.price * producto.quantity}
</h2>
<h2 className="price">${producto.item.price}</h2>
</footer>
</motion.article>
</motion.section>
</motion.div>
</motion.div>
);
})
) : (
<>
<h1
style={{
textAlign: "center",
fontSize: "42px",
borderBottom: "2px solid #ececec",
}}
>
The cart is empty... Start enjoying our products and visit us again
</h1>
<Link to={`/carvel-ecommerce`} className="button-backhome">
GO BACK TO HOME
</Link>
<div
style={{
display: "flex",
textDecoration: "none",
flexDirection: "column",
alignItems: "flex-end",
}}
>
<Link
to={"/carvel-ecommerce/category/Glasses"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Glasses</span>{" "}
<span>
<Glasses className="iconpower" />
</span>
</Link>
<Link
to={"/carvel-ecommerce/category/Hats"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Hats</span>{" "}
<span>
<Hats className="iconpower" />
</span>
</Link>
<Link
to={"/carvel-ecommerce/category/Jackets"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Jackets</span>{" "}
<span>
<Jackets className="iconpower" />
</span>
</Link>
<Link
to={"/carvel-ecommerce/category/Womens"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Womens</span>{" "}
<span>
<Womens className="iconpower" />
</span>
</Link>
{/* <Link
to={"/carvel-ecommerce/category/Mens"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<span>Mens</span>{" "}
<span>
<Mens className="iconpower" />
</span>
</Link> */}
</div>
</>
)}
{addToCarrito.length > 0 ? (
<footer id="site-footer">
<div className="container clearfix">
<div className="right">
{" "}
{pickData}
<h1 className="total">
Total: <span>${totalPrice} </span>
</h1>
<Link to={"/checkout"} className="btn">
Checkout
</Link>
<MdOutlineCleaningServices
onClick={() => clear()}
className="cleared"
/>
</div>
</div>
</footer>
) : (
""
)}
</>
);
};
export default Cart;
I want to render Material UI Elements like MenuItem of a AppBar Conditionally if some conditions are true. In my example menu items should only be rendered for specific users. But it doesnt work like the way it would work with normal html in react.
{context.token && context.userRole === "USER" && (
<MenuItem key="submission" onClick={handleCloseNavMenu}>
<Typography textAlign="center">Submission</Typography>
</MenuItem>
)}
{context.token && context.userRole === "JUDGE" && (
<MenuItem key="submissions" onClick={handleCloseNavMenu}>
<Typography textAlign="center">Submissions</Typography>
</MenuItem>
)}
Its rendering this Items all the time anyways and seems to ignoring the conditions.
The full code:
import React from "react";
import { NavLink } from "react-router-dom";
import AuthContext from "../context/auth-context.js";
import AppBar from "#mui/material/AppBar";
import Box from "#mui/material/Box";
import Toolbar from "#mui/material/Toolbar";
import IconButton from "#mui/material/IconButton";
import Typography from "#mui/material/Typography";
import Menu from "#mui/material/Menu";
import MenuIcon from "#mui/icons-material/Menu";
import Container from "#mui/material/Container";
import Avatar from "#mui/material/Avatar";
import Button from "#mui/material/Button";
import Tooltip from "#mui/material/Tooltip";
import MenuItem from "#mui/material/MenuItem";
import ComputerIcon from "#mui/icons-material/Computer";
const ResponsiveAppBar = (props) => {
const pages = ["Conditions", "Winner", "Submission", "Submissions", "Login"];
const settings = ["Profile", "Settings", "Logout"];
const [anchorElNav, setAnchorElNav] = React.useState(null);
const [anchorElUser, setAnchorElUser] = React.useState(null);
const handleOpenNavMenu = (event) => {
setAnchorElNav(event.currentTarget);
};
const handleOpenUserMenu = (event) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseNavMenu = () => {
setAnchorElNav(null);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
return (
<AuthContext.Consumer>
{(context) => (
<AppBar position="static">
<Container maxWidth="xl">
<Toolbar disableGutters>
<Typography
variant="h6"
noWrap
component="div"
sx={{ mr: 2, display: { xs: "none", md: "flex" } }}
>
<ComputerIcon />
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: "flex", md: "none" } }}>
<IconButton
size="large"
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={handleOpenNavMenu}
color="inherit"
>
<MenuIcon />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorElNav}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
open={Boolean(anchorElNav)}
onClose={handleCloseNavMenu}
sx={{
display: { xs: "block", md: "none" },
}}
>
{context.token && context.userRole === "USER" && (
<MenuItem key="submission" onClick={handleCloseNavMenu}>
<Typography textAlign="center">Submission</Typography>
</MenuItem>
)}
{context.token && context.userRole === "JUDGE" && (
<MenuItem key="submissions" onClick={handleCloseNavMenu}>
<Typography textAlign="center">Submissions</Typography>
</MenuItem>
)}
<MenuItem key="conditions" onClick={handleCloseNavMenu}>
<Typography textAlign="center">Conditions</Typography>
</MenuItem>
<MenuItem key="winner" onClick={handleCloseNavMenu}>
<Typography textAlign="center">Winner</Typography>
</MenuItem>
</Menu>
</Box>
<Typography
variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: "flex", md: "none" } }}
>
LOGO
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
{pages.map((page) => (
<Button
key={page}
onClick={handleCloseNavMenu}
sx={{ my: 2, color: "white", display: "block" }}
>
{page}
</Button>
))}
</Box>
<Box sx={{ flexGrow: 0 }}>
<Tooltip title="Open settings">
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
<Avatar
alt="Remy Sharp"
src="/static/images/avatar/2.jpg"
/>
</IconButton>
</Tooltip>
<Menu
sx={{ mt: "45px" }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
{settings.map((setting) => (
<MenuItem key={setting} onClick={handleCloseNavMenu}>
<Typography textAlign="center">{setting}</Typography>
</MenuItem>
))}
</Menu>
</Box>
</Toolbar>
</Container>
</AppBar>
)}
</AuthContext.Consumer>
);
};
export default ResponsiveAppBar;
How to split material-ui toolbar into left and right part. For example, this is my toolbar
let EnhancedTableToolbar = props => {
const { numSelected, classes ,deletefunc} = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0,
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subtitle1">
{numSelected} selected
</Typography>
) : (
<Typography variant="h6" id="tableTitle">
User List
</Typography>
)}
</div>
<div className={classes.actions}>
{numSelected > 0 ? (
<div >
<div style={{ display: 'inline-block' }}>
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={() => { if (window.confirm('Are you sure you wish to delete '+numSelected +' item?')) {deletefunc()} } }>
</DeleteIcon>
</IconButton>
</Tooltip>
</div>
<div style={{ display: 'inline-block' }}>
<Tooltip title="Edit">
<IconButton aria-label="Edit">
<EditIcon>
</EditIcon>
</IconButton>
</Tooltip>
</div>
</div>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
I want to show the numSelected in my left side of toolbar and the delete button and edit button at my right side of toolbar. However, my example output show the delete button and edit button just beside the numSelected. Anyone has any solution regarding this issue?
The solution is add
flex: '0 0 auto'
in my actions class and a
<div className={classes.spacer}>
between title class and action class.
This is how I setup spacer, title and action classes.
const toolbarStyles = theme => ({
root: {
paddingRight: theme.spacing.unit,
},
highlight:
theme.palette.type === 'light'
? {
color: theme.palette.secondary.main,
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
}
: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.secondary.dark,
},
spacer: {
flex: '1 1 100%',
},
actions: {
color: theme.palette.text.secondary,
flex: '0 0 auto',
},
title: {
flex: '0 0 auto',
},
});