Unable to add scroll functionality - javascript

I get this error while adding a functionality to my web app.
-> so basically there are a few cards on the map and if user 'click' any of the one, the app will scroll to description of that particular card.
And I suppose the error [Violation] 'requestAnimationFrame' handler took 136ms is coming from the following code
const PlaceDetails = ({ place, selected, refProp }) => {
// console.log(place);
const classes = useStyles();
if (selected)
refProp?.current?.scrollIntoView({ behavior: "smooth", block: "start" });
return (
<Card elevation={6}>
<CardMedia
...
map.jsx
return (
<div className={classes.mapContainer}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.REACT_APP_GOOGLE_MAPS_API_KEY }}
// get key from https://console.cloud.google.com/
defaultCenter={coordinates}
center={coordinates}
defaultZoom={14}
margin={[50, 50, 50, 50]}
options={""}
onChange={(e) => {
// console.log(e);
setCoordinates({ lat: e.center.lat, lng: e.center.lng });
setBounds({ ne: e.marginBounds.ne, sw: e.marginBounds.sw });
}}
// as we click a child (card) we want information about which child was the click from the map component all the way to the list component
onChildClick={(child) => setChildClicked(child)}
>
{places?.map((place, i) => (
<div
className={classes.markerContainer}
lat={Number(place?.latitude)}
lng={Number(place?.longitude)}
key={i}
>
List.js
import React, { useState, useEffect, createRef } from "react";
import {
CircularProgress,
Grid,
Typography,
InputLabel,
MenuItem,
FormControl,
Select,
} from "#material-ui/core";
import PlaceDetails from "../PlaceDetails/PlaceDetails";
import useStyles from "./styles";
const List = ({ places, childClicked, isLoading }) => {
const classes = useStyles();
const [type, setType] = useState("restaurants");
const [rating, setRating] = useState("");
const [elRefs, setElRefs] = useState([]);
// console.log({ places });
useEffect(() => {
const refs = Array(places?.length)
.fill()
.map((_, i) => elRefs[i] || createRef());
setElRefs(refs);
}, [places]);
return (
<div className={classes.container}>
{isLoading ? (
<div className={classes.loading}>
<CircularProgress size="5rem" />
</div>
) : (
<>
// ... other code
<Grid container spacing={3} className={classes.list}>
{places?.map((place, i) => (
<Grid item key={i} xs={12}>
<PlaceDetails
place={place}
selected={Number(childClicked) === i}
refProp={elRefs[i]}
/>
</Grid>
))}
</Grid>
</>
)}
</div>
);
};
export default List;
could somebody help me in understanding why the scroll is not working and what error chrome consoles is showing me.

Related

how do I prevent re-render in react

Now I have created this custom hook to perform lazy loading,which takes redux slice action as input and
import { useState, useEffect, useCallback, useRef } from "react";
import { useDispatch } from "react-redux";
function useLazyFetch(fetchAction) {
const dispatch = useDispatch();
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const loadMoreRef = useRef(null);
const handleObserver = useCallback(async(entries) => {
const [target] = entries;
console.log(target.isIntersecting);
if (target.isIntersecting) {
console.log("INTERSECTING.....");
await new Promise((r) => setTimeout(r, 2000));
setPage((prev) => prev + 1);
}
}, []);
useEffect(() => {
const option = {
root: null,
rootMargin: "0px",
threshold: 1.0,
};
const observer = new IntersectionObserver(handleObserver, option);
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
}, [handleObserver]);
const fetchApi = useCallback(async () => {
try {
setLoading(true);
await new Promise((r) => setTimeout(r, 2000));
dispatch(fetchAction(page))
setLoading(false);
} catch (err) {
console.error(err);
}
}, [page,fetchAction,dispatch]);
useEffect(() => {
fetchApi();
}, [fetchApi]);
return { loading, loadMoreRef };
}
export default useLazyFetch;
I am using this in my component like this, here you can see I am tracking div in the bottom using loadMoreRef from useLazyFetch, Now when I am commenting out the fetchApi(); from custom hook its working as expected, on scroll its logging INTERSECTING... in the console but the moment I try to execute the action through fetchApi() my whole app goes into loop,the div tracker with ref comes to top and it fetches the posts but after immediately that action repeats the tracker comes to top and page becomes empty & it fetches next set of posts,I can see that my list is getting appended new set of posts to state in redux dev tool instead of completely setting new state, but in UI it's rendering all posts again and again whic is causing the loop,how can I avoid this ?
import { CircularProgress, Grid, IconButton, Typography } from "#mui/material";
import { Box } from "#mui/system";
import React, { useEffect,useRef,useState } from "react";
import AssistantIcon from "#mui/icons-material/Assistant";
import Post from "../components/Post";
import { useDispatch, useSelector } from "react-redux";
import { getPosts } from "../redux/postSlice";
import AddPost from "../components/AddPost";
import useLazyFetch from "../hooks/useLazyFetch";
export default function Home() {
const dispatch = useDispatch();
// const api = `https://picsum.photos/v2/list`
const { status, posts } = useSelector((state) => state.post);
const {loading,loadMoreRef} = useLazyFetch(getPosts)
useEffect(() => {
dispatch(getPosts());
}, []);
return (
<Box>
<Box borderBottom="1px solid #ccc" padding="8px 20px">
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Typography variant="h6">Home</Typography>
</Grid>
<Grid item>
<IconButton>
<AssistantIcon />
</IconButton>
</Grid>
</Grid>
</Box>
<Box height="92vh" sx={{ overflowY: "scroll" }}>
<AddPost />
<Box textAlign="center" marginTop="1rem">
{status === "loading" && (
<CircularProgress size={20} color="primary" />
)}
</Box>
{status === "success" &&
posts?.map((post) => <Post key={post._id} post={post} />)}
<div style={{height:"50px",width:"100px",backgroundColor:"red"}} ref={loadMoreRef}>{loading && <p>loading...</p>}</div>
</Box>
</Box>
);
}
And here is my redux action & state update part
const initialState = {
status: "idle",
posts: []
};
export const getPosts = createAsyncThunk("post/getPosts", async (page) => {
console.log(page);
console.log("calling api ...");
const { data } = await axios.get(`/api/posts?page=${page}`);
return data;
});
export const postSlice = createSlice({
name: "post",
initialState,
reducers: {},
extraReducers: {
[getPosts.pending]: (state, action) => {
state.status = "loading";
},
[getPosts.fulfilled]: (state, action) => {
state.status = "success";
state.posts = [...state.posts,...action.payload.response.posts] ;
},
[getPosts.rejected]: (state, action) => {
state.status = "failed";
},
}
this is the solution that is working
import { CircularProgress, Grid, IconButton, Typography } from "#mui/material";
import { Box } from "#mui/system";
import React, { useEffect,useMemo } from "react";
import AssistantIcon from "#mui/icons-material/Assistant";
import Post from "../components/Post";
import { useDispatch, useSelector } from "react-redux";
import { getPosts } from "../redux/postSlice";
import AddPost from "../components/AddPost";
import useLazyFetch from "../hooks/useLazyFetch";
export default function Home() {
const { status, posts } = useSelector((state) => state.post);
const {loading,loadMoreRef} = useLazyFetch(getPosts)
const renderedPostList = useMemo(() => (
posts.map((post) => {
return( <Post key={post._id.toString()} post={post} />)
})
), [posts])
return (
<Box>
<Box borderBottom="1px solid #ccc" padding="8px 20px">
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Typography variant="h6">Home</Typography>
</Grid>
<Grid item>
<IconButton>
<AssistantIcon />
</IconButton>
</Grid>
</Grid>
</Box>
<Box height="92vh" sx={{ overflowY: "scroll" }}>
<AddPost />
<Box textAlign="center" marginTop="1rem">
{status === "loading" && (
<CircularProgress size={20} color="primary" />
)}
</Box>
{renderedPostList}
<div style={{height:"50px",width:"100px",backgroundColor:"red"}} ref={loadMoreRef}>{loading && <p>loading...</p>}</div>
</Box>
</Box>
);
}
}
I used useMemo hook to memoize and it works as expected

React - Secondary Prop Value - Node - Material UI

I've been working on simplifying my code and am curious how I would approach passing a secondary value using props and fetching data from the back end. I'm using material UI's Autocomplete and the PERN stack. Everything is working, except I want to change "region_name" to be a prop. So I can change the values dynamically within event details.js. when I'm fetching other data.
I currently have this component setup.
Production.js
import Autocomplete from "#mui/material/Autocomplete";
import Box from "#mui/material/Box";
import Stack from "#mui/material/Stack";
import TextField from "#mui/material/TextField";
export default function CustomAutoComplete(props) {
return (
<Stack sx={{ m: 1 }}>
<Autocomplete
sx={{ ml: 2, mr: 2 }}
size="small"
id="combo-box-demo"
freeSolo
inputValue={props.inputValue}
onInputChange={(event, newValue) => {
props.set(newValue);
}}
getOptionLabel={(data) => `${data.region_name}`}
options={props.data}
isOptionEqualToValue={(option, value) =>
option.region_name === value.region_name
}
renderOption={(props, data) => (
<Box component="li" {...props} key={data.id}>
{data.region_name}
</Box>
)}
renderInput={(params) => <TextField {...params} label="Region" />}
/>
</Stack>
);
}
Then importing it into a separate file EventDetails.js fetching the data and storing it in LocalStorage, which I'll move to useState eventually.
import CustomAutoComplete from "../../atoms/AutoComplete";
import FormGroup from "#mui/material/FormGroup";
import { Fragment, useState, useEffect } from "react";
import { useLocalStorage } from "../../utils/LocalStorage.js";
const EventDetails = () => {
const [region, setRegion] = useLocalStorage("region", "");
const [getRegion, setGetRegion] = useState([]);
// fetching backend data
useEffect(() => {
fetch("/authentication/region")
.then((response) => response.json())
.then((getRegion) => setGetRegion(getRegion));
}, []);
return (
<Fragment>
<FormGroup sx={{ p: 2, m: 1 }}>
<CustomAutoComplete
inputValue={region}
set={setRegion}
data={getRegion}
key={getRegion.id}
name={region_name} // <--feeble attempt
label="Region"
/>
</FormGroup>
</Fragment>
);
};
I had someone help me with finding a solution just had to create a prop variable like
export default function CustomAutoComplete(props) {
const { labelValue } = props;
return (renderOption={(props, data) => (
<Box component="li" {...props} key={data.id}>
{data[labelKey]}
</Box>
)})
then in the parent component
<CustomAutoComplete labelValue="region_name" />

how do i fetch a single category from an arrayObj of categories?

I do have the category id and I can also fetch singleCategory but i'm unable to fetch the product arrayObj within the categories arrayObj?
i want to create a single category page which displays all products in the selected category
all the requests and data is fetched from commerce.js api
import { Grid, Container, Typography } from "#material-ui/core";
import { Link } from "react-router-dom";
import Product from "../Product";
import Spinner from "../Spinner";
import Banner from "../Banner";
import "./style.css";
const Products = ({ categories, addProduct }) => {
const [singleCategory, setSingleCategory] = useState({});
const fetchCategory = async (id) => {
const response = await commerce.categories.retrieve(id);
setSingleCategory({ response });
};
useEffect(() => {
const id = window.location.pathname.split("/");
fetchCategory(id[2]);
}, []);
console.log(singleCategory)
console.log(categories)
if (!categories.length) return <Spinner />;
return (
<div>
<Banner />
<div id="products">
{categories.map((category, index) =>
category.productsData.length ? (
<div
key={category.id}
className="contents"
style={{
backgroundImage:
index % 2 !== 0
? "linear-gradient(to bottom right, #3d4a5d,#3d4a5d, #bb86fc)"
: "",
}}
>
<Container>
<Link className="headline" to={`category-view/${category.id}`}>
<Typography className="headline" variant="h3" component="h2">
{category.name}
</Typography>
</Link>
<Grid container spacing={4}>
{category.productsData.map((product) => (
<Grid key={product.id} item xs={12} sm={6} md={4}>
<Product
product={product}
addProduct={addProduct}
categoryName={category.name}
/>
</Grid>
))}
</Grid>
</Container>
</div>
) : null
)}
</div>
</div>
);
};
export default Products;
this is my app.js
const App = () => {
const [categories, setCategories] = useState([]);
const fetchProducts = async () => {
const { data: products } = await commerce.products.list({ limit: 1000 });
const { data: categoriesData } = await commerce.categories.list();
const productsPerCategory = categoriesData.reduce((acc, category) => {
return [
...acc,
{
...category,
productsData: products.filter((product)=>
product.categories.find((cat) => cat.id === category.id)),
},
];
}, []);
setCategories(productsPerCategory);
};

Why my dispatch is not working for button click event?

I have built a Trello clone using ReactJS, where I have 4 columns called TODO, DOING, DONE and REJECTED, where I can add a card to any column.
In a file I am trying to map over card component and rendering properties from defined dummy data.
What I want to do?
I want to delete a specific card when the button is clicked.
What I tried?
I have added the functionality in my Redux store, but when adding the onclick event to my button, I cannot access the dispatch method which will trigger the deleteCard function.
How do I do that?
My TaskboardList.js component :
import React from "react";
import TaskboardCard from "./TaskboardCard";
import TaskboardActionButton from "./TaskboardActionButton";
import { Droppable } from "react-beautiful-dnd";
const TaskboardList = ({ title, cards, listID }) => {
return (
<Droppable droppableId={String(listID)}>
{provided => (
<div
className="taskboardlist_container"
{...provided.droppableProps}
ref={provided.innerRef}
style={styles.container}
>
<div className="sub-heading">{title}</div>
{cards.map((card, index) => (
<TaskboardCard
key={card.id}
index={index}
text={card.text}
id={card.id}
/>
))}
<TaskboardActionButton listID={listID} />
{provided.placeholder}
</div>
)}
</Droppable>
);
};
const styles = {
container: {
backgroundColor: "#eee",
width: 300,
padding: "0.5rem",
marginRight: "1rem",
height: "100%"
}
};
export default TaskboardList;
My TaskboardCard.js component
import React from "react";
import Card from "#material-ui/core/Card";
import Typography from "#material-ui/core/Typography";
import CardContent from "#material-ui/core/CardContent";
import { Draggable } from "react-beautiful-dnd";
import { connect } from "react-redux";
import { deleteCard } from "../actions";
const TaskboardCard = ({ text, id, index, sample, cardId }) => {
// handleClickDelete = () => {
// // const { dispatch } = this.props;
// // dispatch(deleteCard(cardId));
// console.log("clicked");
// };
return (
<Draggable draggableId={String(id)} index={index}>
{provided => (
<div
className="taskboard_container"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card>
<CardContent>
<Typography style={{ fontSize: "1.5rem" }} gutterBottom>
{text}
</Typography>
</CardContent>
</Card>
{/* //delete added */}
<button
onClick={(cardId, props, sample, dispatch) => {
//const { dispatch } = this.props;
dispatch(deleteCard(cardId));
}}
>
DELETE
</button>
{/* ////////////////////// */}
</div>
)}
</Draggable>
);
};
export default connect()(TaskboardCard);
In the above component delete button is not working because somehow i cannot access the dispatch.
Here is my codesandbox link for further reference to files https://codesandbox.io/s/github/abhinav-anshul/consensolabs
Remove the props and dispatch from onclick event and add dispatch in component parameter list.
If you don't specify the second argument to connect(), your component will receive dispatch by default in porps.
import React from "react";
import Card from "#material-ui/core/Card";
import Typography from "#material-ui/core/Typography";
import CardContent from "#material-ui/core/CardContent";
import { Draggable } from "react-beautiful-dnd";
import { connect } from "react-redux";
import { deleteCard } from "../actions";
const TaskboardCard = ({ text, id, index, sample, cardId, dispatch }) => {
// handleClickDelete = () => {
// // const { dispatch } = this.props;
// // dispatch(deleteCard(cardId));
// console.log("clicked");
// };
return (
<Draggable draggableId={String(id)} index={index}>
{provided => (
<div
className="taskboard_container"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card>
<CardContent>
<Typography style={{ fontSize: "1.5rem" }} gutterBottom>
{text}
</Typography>
</CardContent>
</Card>
{/* //delete added */}
<button
onClick={(cardId, sample) => {
//const { dispatch } = this.props;
dispatch(deleteCard(cardId));
}}
>
DELETE
</button>
{/* ////////////////////// */}
</div>
)}
</Draggable>
);
};
export default connect()(TaskboardCard);

ReactJS Function Component: accordion, allow for multiple selected open with useState

I've mocked a simple logic for an accordion collapsible panels in ReactJS. I'm trying to allow for multiple collapsible to be open but I'm not able to avoid all the collapsible to open and close at once no matter which collapsible has been clicked. This below is the logic for the accordion to allow only one collapsible at the time.
//Accordion.js
import React, { useState } from "react";
import styled, { css } from "styled-components";
import PropTypes from "prop-types";
import Collapse from "./Collapse";
import Header from "./Header";
const Accordion = ({ list, icon}) => {
const [isActiveIndex, setActiveIndex] = useState(null);
const toggleItem = index => {
setActiveIndex(isActiveIndex === index ? null : index);
};
return (
<Wrapper>
{list.map((item, index) => {
const checkOpen = isActiveIndex === index;
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItem}
/>
<Body isOpen={checkOpen}>
<Collapse isOpen={checkOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};
I've created the whole mock in CodeSandBox here: https://codesandbox.io/s/1r2mvk87q
For the initial accordion I'm using useState and checking for the active index - for the allow multiple I guess I should check the previous state of the clicked item but I'm not able to pass the clicked item as the only target for the state to be checked.
//AccordionMultiple.js
const AccordionM = ({ list, icon }) => {
const [isOpen, setOpen] = useState(false);
const toggleItemM = index => {
setOpen(prevState => !prevState);
};
return (
<Wrapper>
{list.map((item, index) => {
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItemM}
/>
<Body isOpen={isOpen}>
<Collapse isOpen={isOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};
In order to allow for multiple collapsible column, you can make use of an object instead of a single index
const Accordion = ({ list, icon}) => {
const [isActivePanel, setActivePanel] = useState({});
const toggleItem = index => {
setActivePanel(prevState => ({...prevState, [index]: !Boolean(prevState[index])}));
};
return (
<Wrapper>
{list.map((item, index) => {
const checkOpen = isActivePanel[index];
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItem}
/>
<Body isOpen={checkOpen}>
<Collapse isOpen={checkOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};

Categories