I am trying to hide the grid item of a certain product and let the others slide in. Right now i am using the display:none property but it hides the item instantaneous. I already have the products filtered and i am checking i want to hide the products that are not filtered using somekind an animation. model:
import React, { useState, useEffect } from "react";
import { Container, Grid, Card, Skeleton, useMediaQuery, Grow, } from "#mui/material";
import Filter from "./Filter";
import Product from "./Product/Product";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { Box } from "#mui/system";
const Products = () => {
const { category } = useParams();
const [sortOption, setSortOption] = useState("name");
const [newProducts, setNewProducts] = useState([]);
const [menu, setMenu] = useState(false);
useEffect(() => {
const selectedSort = sessionStorage.getItem("sortOption") || "name";
setSortOption(selectedSort);
}, []);
const sort = (items, option) => {
switch (option) {
case "name":
return items.sort((a, b) => (a.name > b.name ? 1 : -1));
case "up":
return items.sort((a, b) => (a.price.raw > b.price.raw ? 1 : -1));
case "down":
return items.sort((a, b) => (a.price.raw < b.price.raw ? 1 : -1));
default:
return items.sort((a, b) => (a.name > b.name ? 1 : -1));
}
};
const mobile = useMediaQuery("(max-width:600px)");
const { products, loading } = useSelector((state) => state.products);
let items = products.filter((product) => product.categories[0].slug === category);
let newItems = [];
newProducts.forEach((product) => {
items.forEach((item) => {
if (item.id === product.id) newItems.push(item);
});
});
if (newProducts.length > 0) newItems = sort(newItems, sortOption);
else newItems = sort(items, sortOption);
const renderProducts = () => (
<>
<Container maxWidth="lg" >
<Grid
container
direction="row"
spacing={3}
sx={{
pl: !mobile && menu && "300px",
transition: ".3s",
}}>
{items.map((product) => (
newItems.some(newItem => newItem.id === product.id) ? (
<Grid item xs={12} sm={6} md={4} lg={3} key={product.id} >
<Product product={product} disable={newItems.some(newItem => newItem.id === product.id)} />
</Grid>
) : null
))}
</Grid>
</Container>
</>
);
const loadingView = () => (
<Container maxWidth="lg" >
<Grid
container
direction="row"
spacing={3}
sx={{
pl: !mobile && menu && "300px",
transition: ".3s",
}}>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Skeleton variant="rectangular" width="100%" sx={{ height: ["60vh", "50vh"], mb: 1 }} />
<Box sx={{ p: 2 }}>
<Skeleton variant="text" width="100%" sx={{ height: "20px", mb: 1 }} />
<Card variant="flex" flex="flex">
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", mr: 1 }} />
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", }} />
</Card>
<Skeleton variant="text" width="50%" sx={{ height: "20px" }} />
</Box>
<Skeleton variant="rectangular" width="100%" sx={{ height: "35px" }} />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Skeleton variant="rectangular" width="100%" sx={{ height: ["60vh", "50vh"], mb: 1 }} />
<Box sx={{ p: 2 }}>
<Skeleton variant="text" width="100%" sx={{ height: "20px", mb: 1 }} />
<Card variant="flex" flex="flex">
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", mr: 1 }} />
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", }} />
</Card>
<Skeleton variant="text" width="50%" sx={{ height: "20px" }} />
</Box>
<Skeleton variant="rectangular" width="100%" sx={{ height: "35px" }} />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Skeleton variant="rectangular" width="100%" sx={{ height: ["60vh", "50vh"], mb: 1 }} />
<Box sx={{ p: 2 }}>
<Skeleton variant="text" width="100%" sx={{ height: "20px", mb: 1 }} />
<Card variant="flex" flex="flex">
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", mr: 1 }} />
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", }} />
</Card>
<Skeleton variant="text" width="50%" sx={{ height: "20px" }} />
</Box>
<Skeleton variant="rectangular" width="100%" sx={{ height: "35px" }} />
</Grid>
</Grid>
</Container>
);
return <>
<Filter
sortOption={sortOption}
setSortOption={setSortOption}
menu={menu}
setMenu={setMenu}
products={items}
setNewProducts={setNewProducts}
category={category}
/>
{!loading ? renderProducts() : loadingView()}</>;
};
export default Products;
You need to filter the items as you expect by color or size first.
...
const filteredProducts = newItems.filter(newItem => newItem.id === product.id);
...
<Container maxWidth="lg" >
<Grid
container
// You don't need to use direction="row" since it is default props value
spacing={3}
>
{filteredProducts.map((product) => (
<Grid item xs={12} sm={6} md={4} lg={3} key={product.id}>
<Product product={product} disable={newItems.some(newItem => newItem.id === product.id)} />
</Grid>
))}
</Grid>
</Container>
...
If you want to filter the products more accurately, then you could build a customer filter function. Please refer to this.
Related
So I want when the user clicks on the button, be able to create new CatagoryField Component that I made. When I place the component in a function and call it it will only create the component once. I will appreciate some help. I'm confused about how should I implement this?
App components
import React, { useState } from "react";
import {
Accordion,
AccordionSummary,
Chip,
Button,
IconButton,
Autocomplete,
TextField,
} from "#mui/material";
import { Grid } from "#mui/material";
import SearchBar from "material-ui-search-bar";
import DeleteIcon from "#mui/icons-material/Delete";
import ExpandMoreOutlinedIcon from "#mui/icons-material/ExpandMoreOutlined";
import AddCircleOutlineOutlinedIcon from "#mui/icons-material/AddCircleOutlineOutlined";
import HelpIcon from "#mui/icons-material/Help";
import HistoryToggleOffIcon from "#mui/icons-material/HistoryToggleOff";
import AllMembers from "./components/common/main/allMembers";
import CatagoryField from "./components/common/main/catagoryField";
const autoCompleteOptions = [
{ title: "A", year: 1994 },
{ title: "B", year: 1972 },
{ title: "C", year: 1974 },
{ title: "D", year: 2008 },
{ title: "E", year: 1957 },
{ title: "F", year: 1993 },
{ title: "G", year: 1994 },
];
const App = () => {
const [value, setValue] = useState();
const [element, setElement] = useState(0);
const doSomethingWith = () => {
return null;
};
let i = 0;
const creator = () => {
while (i < element) {
i++;
return (
<CatagoryField
catagoryName="خدماتی"
react="از آنها چیزی میخریم"
createdBy="سیستم"
disable={false}
/>
);
}
};
console.log(element);
return (
<>
<div style={{ margin: "4rem 1rem" }}>
<Accordion>
<AccordionSummary
sx={{ height: "65px" }}
aria-controls="panel1a-content"
>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<IconButton className="chevron__icon">
<ExpandMoreOutlinedIcon />
</IconButton>
<span className="users__catagory"> دسته بندی کاربران</span>
</Grid>
<Grid item>
<IconButton>
<HistoryToggleOffIcon />
</IconButton>
<IconButton>
<HelpIcon className="help" />
</IconButton>
<span className="version">4.3.2</span>
</Grid>
</Grid>
</AccordionSummary>
<AccordionSummary
sx={{
height: "65px",
padding: "30px",
background: "#E6E6E6",
cursor: "default !important",
}}
aria-controls="panel1a-content"
>
<Grid
container
direction="row"
alignItems="center"
sx={{ display: "relative" }}
>
<Grid item>
<Button
variant="outlined"
sx={{ height: "50px" }}
size="medium"
onClick={() => setElement(element + 1)}
endIcon={
<AddCircleOutlineOutlinedIcon
sx={{ marginRight: "10px" }}
/>
}
>
افزودن دسته جدید
</Button>
</Grid>
<Grid sx={{ width: "207px" }} item>
<SearchBar
className="search__holder"
placeholder="جستجو..."
value={value}
style={{ width: "207px", height: "45px" }}
onChange={(newValue) => setValue(newValue)}
onRequestSearch={() => doSomethingWith()}
/>
</Grid>
<Grid className="sort__field" item>
<Autocomplete
sx={{
width: "207px !important",
padding: "0 !important",
backgroundColor: "white !important",
}}
options={autoCompleteOptions}
getOptionLabel={(option) => option.title}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
label={option.title}
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
sx={{
width: "207px !important",
padding: "0 !important",
background: "white !important",
}}
placeholder="مرتب سازی..."
/>
)}
/>
</Grid>
<Grid item>
<IconButton sx={{ margin: "0 5px" }}>
<DeleteIcon />
</IconButton>
</Grid>
<Grid item className="last__edit">
<Grid item>
<span>
آخرین ویرایش:
<a className="last_Modified" href="#">
سیستم
</a>
</span>
<span>1405/12/24 23:59 </span>
</Grid>
</Grid>
</Grid>
</AccordionSummary>
<AllMembers />
<CatagoryField
catagoryName="اپراتور سیستم"
react="اپراتور سیستم"
createdBy="سیستم"
/>
<CatagoryField
catagoryName="مشتریان"
react="به آنها چیزی فروخته ایم"
createdBy="سیستم"
/>
<CatagoryField
catagoryName="خدماتی"
react="از آنها چیزی میخریم"
createdBy="سیستم"
disable={false}
/>
{creator()}
</Accordion>
</div>
</>
);
};
export default App;
The best way in order to achieve the solution is to use a state with useMemo.and the problem with your code is that the creator acts as a function and it will only execute once try this.
import {useMemo} from 'react';
const App = () => {
const [elements , setElements] = useState(0);
const creator = useMemo(() => {
return (
<>
{Array(elements).fill(elements).map((item , index) => (
<CategoryField
key={`cat-${index}`} // key is need when mapping! it is not props
catagoryName="خدماتی"
react="از آنها چیزی میخریم"
createdBy="سیستم"
disable={false}
/>
))}
</>
)
},[elements]);//render when elements change
return (
<>
<div style={{ margin: "4rem 1rem" }}>
<Accordion>
<AccordionSummary
sx={{ height: "65px" }}
aria-controls="panel1a-content"
>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<IconButton className="chevron__icon">
<ExpandMoreOutlinedIcon />
</IconButton>
<span className="users__catagory"> دسته بندی کاربران</span>
</Grid>
<Grid item>
<IconButton>
<HistoryToggleOffIcon />
</IconButton>
<IconButton>
<HelpIcon className="help" />
</IconButton>
<span className="version">4.3.2</span>
</Grid>
</Grid>
</AccordionSummary>
<AccordionSummary
sx={{
height: "65px",
padding: "30px",
background: "#E6E6E6",
cursor: "default !important",
}}
aria-controls="panel1a-content"
>
<Grid
container
direction="row"
alignItems="center"
sx={{ display: "relative" }}
>
<Grid item>
<Button
variant="outlined"
sx={{ height: "50px" }}
size="medium"
onClick={() => setElements(elements + 1)}
endIcon={
<AddCircleOutlineOutlinedIcon
sx={{ marginRight: "10px" }}
/>
}
>
افزودن دسته جدید
</Button>
</Grid>
<Grid sx={{ width: "207px" }} item>
<SearchBar
className="search__holder"
placeholder="جستجو..."
value={value}
style={{ width: "207px", height: "45px" }}
onChange={(newValue) => setValue(newValue)}
onRequestSearch={() => doSomethingWith()}
/>
</Grid>
<Grid className="sort__field" item>
<Autocomplete
sx={{
width: "207px !important",
padding: "0 !important",
backgroundColor: "white !important",
}}
options={autoCompleteOptions}
getOptionLabel={(option) => option.title}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
key={`chip-${index}`}
variant="outlined"
label={option.title}
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
sx={{
width: "207px !important",
padding: "0 !important",
background: "white !important",
}}
placeholder="مرتب سازی..."
/>
)}
/>
</Grid>
<Grid item>
<IconButton sx={{ margin: "0 5px" }}>
<DeleteIcon />
</IconButton>
</Grid>
<Grid item className="last__edit">
<Grid item>
<span>
آخرین ویرایش:
<a className="last_Modified" href="#">
سیستم
</a>
</span>
<span>1405/12/24 23:59 </span>
</Grid>
</Grid>
</Grid>
</AccordionSummary>
<AllMembers />
<CatagoryField
catagoryName="اپراتور سیستم"
react="اپراتور سیستم"
createdBy="سیستم"
/>
<CatagoryField
catagoryName="مشتریان"
react="به آنها چیزی فروخته ایم"
createdBy="سیستم"
/>
<CatagoryField
catagoryName="خدماتی"
react="از آنها چیزی میخریم"
createdBy="سیستم"
disable={false}
/>
{creator}
</Accordion>
</div>
</>
);
}
export default App;
what my task is I am using TextFiled according to the Array but the issue is not able to change this all value dynamically I am aware I can use the index to this task based on ternary conditions.
const TextField = () => {
return (
<>
<Card sx={{ mt: 4 }} variant="outlined">
<CardContent>
<Grid container spacing={{ xs: 2, md: 3 }}>
{Array.fill("").map((data, index) => (
<Grid item xs={12} sm={6} md={3} key={index}>
<TextField
id="outlined-basic"
label={data}
name={index === 0}
size="small"
variant="outlined"
value={data}
/>
</Grid>
))}
</Grid>
</CardContent>
</Card>
</>
);
};
return (
<>
<Card sx={{ mt: 4 }} variant="outlined">
<CardContent>
<Grid container spacing={{ xs: 2, md: 3 }}>
{Array.from([
"organisationName",
"organisationID",
"manager",
"branch",
"product"
]).map((data, index) => (
<Grid item xs={12} sm={6} md={3} key={index}>
<TextField
id="outlined-basic"
label={data}
name="organisationName"
size="small"
variant="outlined"
value={teamForm.values.data}
onBlur={teamForm.handleBlur}
onChange={teamForm.handleChange.bind(this)}
error={Boolean(teamForm.errors.data)}
helperText={teamForm.errors.data}
/>
</Grid>
))}
</Grid>
</CardContent>
<CardActions>
<Box
display="flex"
flexGrow={1}
alignItems="center"
justifyContent="flex-end"
flexDirection="row"
>
<Button
variant="contained"
color="primary"
onClick={teamForm.handleSubmit}
>
Add team
</Button>
</Box>
</CardActions>
</Card>
</>
)
import React from "react";
import {
Grid,
Card,
CardContent,
Box,
CardActions,
TextField,
Button
} from "#mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
const validationSchema = Yup.object().shape({
organisationName: Yup.string().required("Required!"),
organisationID: Yup.string().required("Required!"),
manager: Yup.string().required("Required!")
});
const AddNewTeam = () => {
const teamForm = useFormik({
initialValues: {
organisationName: "",
organisationID: "",
manager: "",
branch: "",
product: ""
},
validationSchema,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
}
});
console.log(teamForm);
return (
<>
<Card sx={{ mt: 4 }} variant="outlined">
<CardContent>
<Grid container spacing={{ xs: 2, md: 3 }}>
{Array.from([
"organisationName",
"organisationID",
"manager",
"branch",
"product"
]).map((data, index) => (
<Grid item xs={12} sm={6} md={3} key={index}>
<TextField
id="outlined-basic"
label={data}
name="organisationName"
size="small"
variant="outlined"
value={teamForm.values.data}
onBlur={teamForm.handleBlur}
onChange={teamForm.handleChange.bind(this)}
error={Boolean(teamForm.errors.data)}
helperText={teamForm.errors.data}
/>
</Grid>
))}
</Grid>
</CardContent>
<CardActions>
<Box
display="flex"
flexGrow={1}
alignItems="center"
justifyContent="flex-end"
flexDirection="row"
>
<Button
variant="contained"
color="primary"
onClick={teamForm.handleSubmit}
>
Add team
</Button>
</Box>
</CardActions>
</Card>
</>
);
};
export default AddNewTeam;
I have create an dynamic input select field ,in that field creating editable I am try to bind some data predefined But I am set Data but it takes only index but values are not updated in fields.
My code: https://codesandbox.io/s/dynamic-select-update-n1k9v
Input Fields Data
const [roomInputs, setRoomInputs] = useState([
{ boardBasic: "", roomType: "", adult: "", child: "" }
]);
Predefined Data to set in input fields
const updateData = [
{
id: "1",
boardBasic: "roomOnly",
roomType: "Delux Room",
adult: "2",
child: "2"
},
{
id: "2",
boardBasic: "fullBoard",
roomType: "Delux Room",
adult: "2",
child: "2"
}
];
useEffect(() => {
roomDataInput();
}, []);
const roomDataInput = () => {
setRoomInputs(updateData);
};
Input Fields
<form onSubmit={handleSubmit}>
{roomInputs.map((x, i) => (
<div key={i}>
<Grid container spacing={2}>
<Grid item sm={12} lg={2} xs={12}>
<Select
name="roomType"
placeholder="Room Type"
isSearchable
value={options.value}
options={options2}
onChange={(option) =>
handleRoomChangeType(option, i, "roomType")
}
/>
</Grid>
<Grid item lg={2} sm={12} xs={12}>
<Select
name="boardBasic"
placeholder="Board Basic"
value={options.value}
onChange={(option) =>
handleRoomChangeBoard(option, i, "boardBasic")
}
options={BoardBasic}
/>
</Grid>
<Grid item sm={12} lg={2} xs={12}>
<Select
name="adult"
placeholder="Adult"
value={options.value}
onChange={(option) =>
handleRoomChangeAdult(option, i, "adult")
}
options={options}
/>
</Grid>
<Grid item sm={12} lg={2} xs={12}>
<Select
name="child"
placeholder="Child"
value={options.value}
onChange={(option) =>
handleRoomChangeChild(option, i, "child")
}
options={options}
/>
</Grid>
<Grid item sm={12} lg={2} xs={12}>
{roomInputs.length !== 1 && (
<DeleteIcon
onClick={() => handleRemoveClickRoom(i)}
style={{
marginRight: "10px",
marginTop: "4px",
cursor: "pointer"
}}
/>
)}
{roomInputs.length - 1 === i && (
<AddCircleOutlineIcon
onClick={handleAddClickRoom}
style={{ marginTop: "4px", cursor: "pointer" }}
/>
)}
</Grid>
</Grid>
</div>
))}
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</form>
You should change default state value to updateData and remove useEffect:
const [roomInputs, setRoomInputs] = useState(updateData);
// useEffect(() => {
// roomDataInput();
// }, []);
Also set defaultValue for all the selects in the map:
{roomInputs.map((x, i) => (
<div key={i}>
<Grid container spacing={2}>
<Grid item sm={12} lg={2} xs={12}>
<Select
defaultValue={options2.find((y) => y.value == x.roomType)}
name="roomType"
placeholder="Room Type"
isSearchable
value={options.value}
options={options2}
onChange={(option) =>
handleRoomChangeType(option, i, "roomType")
}
/>
</Grid>
<Grid item lg={2} sm={12} xs={12}>
<Select
defaultValue={BoardBasic.find(
(y) => y.value === x.boardBasic
)}
name="boardBasic"
placeholder="Board Basic"
value={options.value}
onChange={(option) =>
handleRoomChangeBoard(option, i, "boardBasic")
}
options={BoardBasic}
/>
</Grid>
<Grid item sm={12} lg={2} xs={12}>
<Select
defaultValue={options.find((y) => y.value == x.adult)}
name="adult"
placeholder="Adult"
value={options.value}
onChange={(option) =>
handleRoomChangeAdult(option, i, "adult")
}
options={options}
/>
</Grid>
<Grid item sm={12} lg={2} xs={12}>
<Select
defaultValue={options.find((y) => y.value == x.child)}
name="child"
placeholder="Child"
value={options.value}
onChange={(option) =>
handleRoomChangeChild(option, i, "child")
}
options={options}
/>
</Grid>
<Grid item sm={12} lg={2} xs={12}>
{roomInputs.length !== 1 && (
<DeleteIcon
onClick={() => handleRemoveClickRoom(i)}
style={{
marginRight: "10px",
marginTop: "4px",
cursor: "pointer"
}}
/>
)}
{roomInputs.length - 1 === i && (
<AddCircleOutlineIcon
onClick={handleAddClickRoom}
style={{ marginTop: "4px", cursor: "pointer" }}
/>
)}
</Grid>
</Grid>
</div>
))}
I currently have a search input which filters through the github repos. I then have a drop down select which allows a user to filter based on the language of the code used. I instead would like to use buttons rather than a drop down. Is there a way to filter the results on click as opposed to onChange like I am doing with the drop down. My code is as follows:
const Profile = () => {
const [formData, setFormData] = useState(INITIAL_STATE)
const [updated, setUpdated] = useState(false)
const [created, setCreated] = useState(false)
const { data } = useContext(GithubContext)
const handleUpdated = () => {
setUpdated(!updated)
data &&
data.sort((a, b) => {
if (updated) return a.updated_at > b.updated_at ? -1 : 1
return a.updated_at > b.updated_at ? 1 : -1
})
}
const handleCreated = () => {
setCreated(!created)
data &&
data.sort((a, b) => {
if (created) return a.created_at > b.created_at ? -1 : 1
return a.created_at > b.created_at ? 1 : -1
})
}
const handleInputChange = field => e => {
setFormData({ ...formData, [field]: e.target.value })
}
const classes = useStyles()
return (
<>
<div style={{ marginTop: 85, marginBottom: 85 }}>
<Container className={classes.dashboardContainer}>
<Card className={classes.card} style={{ width: '100%' }}>
<CardContent className={classes.content}>
<div className={classes.form}>
<Grid
container
spacing={2}
alignItems='center'
justify='space-between'
>
<Grid item sm={4} xs={12} className={classes.grid}>
<SelectStatus
language={formData.language}
handleInputChange={handleInputChange}
/>
</Grid>
<Grid item sm={4} xs={12} className={classes.grid}>
<TextField
className={classes.jobField}
margin='normal'
fullWidth
id='search'
name='search'
label='Search by Title'
placeholder='Search by Title'
onChange={handleInputChange('search')}
value={formData.search}
/>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
variant='contained'
color='primary'
onClick={handleUpdated}
>
Updated {updated ? '(oldest)' : '(newest)'}
</Button>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
fullWidth
variant='contained'
color='primary'
onClick={handleCreated}
>
Created {created ? '(oldest)' : '(newest)'}
</Button>
</Grid>
</Grid>
</div>
</CardContent>
</Card>
<div
style={{
textAlign: 'center'
}}
>
<Button variant='outlined' color='primary'>
JavaScript
</Button>
<Button variant='outlined' color='primary'>
Primary
</Button>
<Button variant='outlined' color='primary'>
Secondary
</Button>
<Button variant='outlined' color='primary'>
Disabled
</Button>
<Button variant='outlined' color='primary'>
Link
</Button>
<br />
</div>
</Container>
{!data ? (
<h1 className={classes.loading}>Initializing Repos...</h1>
) : (
<Container style={{ padding: 10 }}>
{!data ? (
<div style={{ placeItems: 'center' }}>Loading...</div>
) : (
<Grid container alignItems='center' spacing={4}>
{data &&
data
.filter(data => {
if (formData.language === 'All') return true
return data.language === formData.language
})
.filter(data => {
if (formData.search === '') return true
return (data.name + data.language)
.toLowerCase()
.includes(formData.search.toLowerCase())
})
.map(user => <RepoCard key={user.id} user={user} />)}
</Grid>
)}
</Container>
)}
</div>
</>
)
}
export default Profile
and here is the drop down component:
import React, { useRef } from "react"
// MUI stuff
import Select from "#material-ui/core/Select"
import InputLabel from "#material-ui/core/InputLabel"
import MenuItem from "#material-ui/core/MenuItem"
import FormControl from "#material-ui/core/FormControl"
const SelectStatus = ({ language, handleInputChange }) => {
const inputLabel = useRef(null)
return (
<FormControl style={{ width: "100%" }}>
<InputLabel ref={inputLabel} id='demo-simple-select-outlined-label'>
Status
</InputLabel>
<Select
labelId='demo-simple-select-outlined-label'
id='demo-simple-select-outlined'
value={language}
onChange={handleInputChange("language")}
fullWidth
>
<MenuItem value='All'>All</MenuItem>
<MenuItem value='HTML'>HTML</MenuItem>
<MenuItem value='JavaScript'>JavaScript</MenuItem>
<MenuItem value='Ruby'>Ruby</MenuItem>
</Select>
</FormControl>
)
}
export default SelectStatus
Any suggestions would be extremely helpful!!!
You can just use buttons and add them a value so it will work exactly the same as the dropdown
const handleInputChange = field => e => {
setFormData({ ...formData, [field]: e.target.value })
}
<Button onClick={handleInputChange('language')} value="All">All</Button>
<Button onClick={handleInputChange('language')} value="HTML")>HTML</Button>
<Button onClick={handleInputChange('Javascript')} value="Javascript">JavaScript</Button>
<Button onClick={handleInputChange('Ruby')} value="Ruby">Ruby</Button>
I have a component that scrolls to view after a comment has been submitted.
const commentSubmit = (e: any, id: number) => {
......
divRef.current.scrollIntoView({ behavior: "smooth" });
};
It scrolls to the comment where <div ref={divRef]></div> is placed which is at the bottom of the most recent comment. The probelem is that if i make a new post, it will scroll into the wrong div element. So its just scrolling at the very bottom. How can i tell ref to scroll to the a specific element based on what post the comment belongs to ?
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
// ignore this divRef in comment list its not being called in the comment list.
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
<div ref={divRef}></div>
postItemContainer.tsx
import React, { Fragment, useState, useCallback, useRef } from "react";
import Avatar from "#material-ui/core/Avatar";
import Button from "#material-ui/core/Button";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import Typography from "#material-ui/core/Typography";
import DeleteOutlineOutlinedIcon from "#material-ui/icons/DeleteOutlineOutlined";
import FavoriteIcon from "#material-ui/icons/Favorite";
import FavoriteBorderIcon from "#material-ui/icons/FavoriteBorder";
import moment from "moment";
import { toast, ToastContainer } from "react-toastify";
import OurLink from "../../../common/OurLink";
import CommentForm from "../comment/CommentForm";
import CommentList from "../commentList/CommentList";
import OurModal from "../../../common/OurModal";
import "react-toastify/dist/ReactToastify.css";
function PostItemContainer(props: any) {
const [openModal, setOpenModal] = useState(false);
const [openForm, setOpenForm] = useState(false);
const [comment_body, setCommentBody] = useState("");
const [gifUrl, setGifUrl] = useState("");
const divRef = React.useRef<any>();
const writeComment = () => {
// this is the same as this.setState({ openForm: !this.state.open })
setOpenForm(!openForm);
};
const commentChange = (comment) => {
setGifUrl("");
setCommentBody(comment);
};
const selectGif = (e) => {
setGifUrl(e.images.downsized_large.url);
setCommentBody("");
// you wont be able to add text comment with a gif, it will look weird :(
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
const commentSubmit = (e: any, id: number) => {
e.preventDefault();
const formData = {
comment_body,
id,
gifUrl,
};
props.postComment(formData);
setCommentBody("");
setOpenForm(false);
console.log(divRef);
divRef.current.scrollIntoView({ behavior: "smooth" });
};
const { post, currentUser, getNotifications } = props;
console.log(divRef);
return (
<Fragment>
{getNotifications && <ToastContainer autoClose={1000} position={toast.POSITION.BOTTOM_RIGHT} />}
<Grid item={true} sm={12} md={12} style={{ margin: "20px 0px" }}>
<Paper style={{ padding: "20px" }}>
<Typography variant="h5" align="left">
<OurLink
style={{ fontSize: "16px" }}
to={{
pathname: `/post/${post.id}`,
state: { post },
}}
title={post.title}
/>
</Typography>
<Grid item={true} sm={12} md={12} style={{ padding: "30px 0px" }}>
<Typography align="left">{post.postContent.slice(0, 50)}</Typography>
</Grid>
<Avatar
style={{
display: "inline-block",
margin: "-10px -20px",
padding: "0px 30px 0px 20px",
}}
sizes="small"
src={post.author.gravatar}
/>
<Typography display="inline" variant="subtitle1" align="left">
<OurLink
to={{
pathname: `/profile/${post.author.username}`,
state: { post },
}}
title={post.author.username}
/>
</Typography>
<Typography align="right">Likes: {post.likeCounts}</Typography>
<Grid container={true} spacing={1} style={{ padding: "20px 0px" }}>
<Grid item={true} sm={10} lg={10} md={10} style={{ padding: "0px 0px" }}>
<Typography align="left">
{currentUser && currentUser.user && post.userId === currentUser.user.id ? (
<span style={{ cursor: "pointer" }} onClick={() => props.deletePost(post.id, post.userId)}>
<DeleteOutlineOutlinedIcon style={{ margin: "-5px 0px" }} color="primary" /> <span>Delete</span>
</span>
) : null}
</Typography>
</Grid>
<Grid item={true} sm={2} lg={2} style={{ padding: "0px 15px" }}>
<Typography align="right">
{Object.entries(currentUser).length === 0 ? (
<Fragment>
<span onClick={handleClickOpen}>
<FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
</span>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
{post.likedByMe === true ? (
<span style={{ cursor: "pointer" }} onClick={() => props.dislikePost(post.id)}>
<FavoriteIcon style={{ color: "red" }} />
</span>
) : (
<span onClick={() => props.likePost(post.id)}>
<FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
</span>
)}
</Fragment>
)}
</Typography>
</Grid>
</Grid>
<Typography variant="h6" align="left">
{moment(post.createdAt).calendar()}
</Typography>
<Grid item={true} sm={12} lg={12} style={{ paddingTop: "40px" }}>
{Object.entries(currentUser).length === 0 ? (
<Fragment>
<Button onClick={handleClickOpen} variant="outlined" component="span" color="primary">
{"Write A Comment"}
</Button>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<Button onClick={writeComment} variant="outlined" component="span" color="primary">
{openForm ? "Close" : "Write A Comment"}
</Button>
</Fragment>
)}
{openForm ? (
<CommentForm
commentChange={(e: any) => commentChange(e.target.value)}
comment_body={comment_body}
onSubmit={(e) => commentSubmit(e, post.id)}
gifUrl={selectGif}
isGif={gifUrl}
/>
) : null}
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
<div ref={divRef}></div>
</Grid>
</Paper>
</Grid>
</Fragment>
);
}
export default PostItemContainer
CommentList.tsx(just in case) how the comments are bing mapped and iterated
import React, { Fragment, useState } from "react";
import Grid from "#material-ui/core/Grid";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import CommentListContainer from "../commentListContainer/commentListContainer";
function CommentList(props: any) {
const [showMore, setShowMore] = useState<Number>(2);
const [openModal, setOpenModal] = useState(false);
const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
const the_comments = props.comments.length;
const inc = showMore as any;
const min = Math.min(2, the_comments - inc);
const showComments = (e) => {
e.preventDefault();
if (inc + 2 && inc <= the_comments) {
setShowMore(inc + 2);
setShowLessFlag(true);
} else {
setShowMore(the_comments);
}
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
const showLessComments = (e) => {
e.preventDefault();
setShowMore(2);
setShowLessFlag(false);
};
const isBold = (comment) => {
return comment.userId === props.userId ? 800 : 400;
};
// show comments by recent, and have the latest comment at the bottom, with the previous one just before it.
const filterComments = props.comments
.slice(0)
.sort((a, b) => {
const date1 = new Date(a.createdAt) as any;
const date2 = new Date(b.createdAt) as any;
return date2 - date1;
})
.slice(0, inc)
.reverse();
const showMoreComments = () => {
return filterComments.map((comment, i) => (
<div key={i}>
<CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
</div>
));
};
return (
<Grid>
<Fragment>
<div style={{ margin: "30px 0px" }}>
{props.comments.length > 2 ? (
<Fragment>
{min !== -1 && min !== -2 ? (
<Fragment>
{min !== 0 ? (
<OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
View {min !== -1 && min !== -2 ? min : 0} More Comments
</OurSecondaryButton>
) : (
<OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
Show Less Comments
</OurSecondaryButton>
)}
</Fragment>
) : (
<OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
Show Less Comments
</OurSecondaryButton>
)}
</Fragment>
) : null}
</div>
</Fragment>
{showLessFlag === true ? (
// will show most recent comments below
showMoreComments()
) : (
<Fragment>
{/* filter based on first comment */}
{filterComments.map((comment, i) => (
<div key={i}>
<CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
</div>
))}
</Fragment>
)}
</Grid>
);
}
// prevents un-necesary re renders
export default React.memo(CommentList);
The question seems quite complicated, but i over complicated it.
What i had to do was add this
setTimeout(() => {
divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
}, 1500);
in the commentSubmit method, this will scroll to the recent comment.
const commentSubmit = (e: any, id: number) => {
e.preventDefault();
const formData = {
comment_body,
id,
gifUrl,
};
props.postComment(formData);
setCommentBody("");
setOpenForm(false);
console.log(divRef);
// divRef.current.scrollIntoView({ behavior: "smooth" });
// my attempt to scroll to the lastest comment.
setTimeout(() => {
divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
}, 1500);
}
And make some adjustments to the code, if there are no comments divRef will throw an error upon adding a comment, because it is looking for a comment but there aren't any comments, so we will do this if there are comments less than 1 or 2. In my case im using 2 cause im showing 2 comments initially and i have a filter that shows more comments, and show less.
{post.Comments.length === 0 ? <div ref={divRef}></div> : null}
reference scrollIntoView Scrolls just too far
Rest of the code
{post.Comments.length === 0 ? <div ref={divRef}></div> : null}
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
{post.Comments.length < 2 ? <div ref={divRef}></div> : null}