How to reduce the height of carousel - javascript

Front-end development is a new experience for me, for that purpose I prefer to go with react as a goto library. Now I am stuck in a problem.
Can anyone guide me on how to reduce the height of my carousel images without disrupting the quality of my pictures?
Desired output:
Here is my code,
import React from 'react';
import { Gallery, GalleryImage } from "react-gesture-gallery";
import ReactDOM from "react-dom";
const images = [
"https://scontent.fkhi2-1.fna.fbcdn.net/v/t1.0-9/77017766_759244471260597_2356862208463339520_o.jpg?_nc_cat=109&_nc_ohc=UQbWRlHdFO4AQknJyvWGAbIif0SgcS5n49AOU3TRgnqOvh9cPMwZ-uY9A&_nc_ht=scontent.fkhi2-1.fna&oh=5bba2ef0d536b1d3752a12825cac2e88&oe=5E465DD2",
"https://scontent.fkhi2-1.fna.fbcdn.net/v/t1.0-9/76727026_759558941229150_3910702859859001344_o.jpg?_nc_cat=104&_nc_ohc=jrAaYH_HT2MAQn8cSekMJqFSo9ZoXP5AQwjtn6JgZ1gDPDH1441VSIeFA&_nc_ht=scontent.fkhi2-1.fna&oh=536d5190623a82400bf8b7f11f71b7fb&oe=5E3EE66A"
];
function Home() {
const [index, setIndex] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => {
if (index === 4) {
setIndex(0);
} else {
setIndex(prev => prev + 1);
}
}, 3000);
return () => clearInterval(timer);
}, [index]);
return (
<Gallery
style={{
background: "black",
height: "100vh",
width: "100vw"
}}
index={index}
onRequestChange={i => {
setIndex(i);
}}
>
{images.map(image => (
<GalleryImage objectFit="contain" key={image} src={image} />
))}
</Gallery>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Home />, rootElement);
export default Home;

Related

How to hide splash screen once images are loaded

I am using expo and want to show the splash screen until images are all loaded. I am loading images from a url.
What is supposed to happen is when onLoad is called, numberOfImagesLoaded increases by 1, and useAppIsReady is run again. Then there is a check
const imagesAreLoaded = numberOfImagesLoaded > 2;.
When numberOfImagesLoaded > 2, appIsReady is true, then onLayoutRootView gets called and the splash screen gets hidden.
However, I am not sure why when onLoad is called, numberOfImagesLoaded in useAppIsReady does not seem to increase.
App.js
import { StatusBar } from "expo-status-bar";
import { Image, StyleSheet, Text, View } from "react-native";
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import React, { useCallback, useState } from "react";
import useAppIsReady from "./useAppIsReady";
import data from "./data";
SplashScreen.preventAutoHideAsync();
export default function App() {
const [numberOfImagesLoaded, setNumberOfImagesLoaded] = useState(0);
const appIsReady = useAppIsReady(numberOfImagesLoaded);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<View onLayout={onLayoutRootView} style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Images setNumberOfImagesLoaded={setNumberOfImagesLoaded} />
<StatusBar style="auto" />
</View>
);
}
const Images = ({ setNumberOfImagesLoaded }) => {
return (
<>
{data.map(({ imgSrc }) => {
return (
<MyImage
key={imgSrc}
imgSrc={imgSrc}
setNumberOfImagesLoaded={setNumberOfImagesLoaded}
/>
);
})}
</>
);
};
const MyImage = ({ imgSrc, setNumberOfImagesLoaded }) => {
const onLoad = () => {
setNumberOfImagesLoaded((prev) => prev + 1);
};
return (
<Image source={{ uri: imgSrc }} style={styles.image} onLoad={onLoad} />
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
image: {
flex: 1,
width: "100%",
},
});
useAppIsReady.js
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import React, { useState, useCallback } from "react";
const useAppIsReady = (numberOfImagesLoaded) => {
const [appIsReady, setAppIsReady] = useState(false);
// Load fonts
const baseAssetsPath = "./assets/fonts";
const fonts_by_path = {
"DMSans-Regular": require(`${baseAssetsPath}/DMSans-Regular.ttf`),
"DMSans-Bold": require(`${baseAssetsPath}/DMSans-Bold.ttf`),
"DMSans-BoldItalic": require(`${baseAssetsPath}/DMSans-BoldItalic.ttf`),
"DMSans-Medium": require(`${baseAssetsPath}/DMSans-Medium.ttf`),
"DMSans-MediumItalic": require(`${baseAssetsPath}/DMSans-MediumItalic.ttf`),
"DMSans-Italic": require(`${baseAssetsPath}/DMSans-Italic.ttf`),
};
const [fontsLoaded] = useFonts(fonts_by_path);
// Load images
console.log(numberOfImagesLoaded);
const imagesAreLoaded = numberOfImagesLoaded > 2;
// const imagesAreLoaded = true;
if (fontsLoaded && imagesAreLoaded && !appIsReady) {
setAppIsReady(true);
}
return appIsReady;
};
export default useAppIsReady;
Here is a link to the repo
https://github.com/Bijig0/loading-imgs-testing

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

Strange State / UseEffect Behaviour using NextJS Link

I am experiencing so really strange behaviour in this personal project i am doing. In short it is a recipe website, with buttons at the top that direct to different pages that ultimately query firebase and pull down a query.
Index.js File - Queries Firestore passes props to FoodCard.js to Render a list of all recipes
breakfast.js - Queries Firestore with a filter and passes same props down (with different results) to FoodCard.js
Behaviour
When i click on Breakfast JS, it brings up my list of filtered results correctly, however when i click my Next Link "Welcome to the Family Heirloom" to return to the index the first click doesnt respond, then the second click returns home, but with the filtered breakfast result concatonated with all the original results (effectively causing duplicates)Index on First Render
Breakfast filter successful
index with the now duplicate pancake result
I have messed about wondering if useEffect is not getting triggered which you may see in the code, but that didnt seem to work, so at a loss
Index.js
import { React, useEffect, useState } from 'react'
import { useRouter } from 'next/dist/client/router'
import { useTheme } from '#mui/material/styles'
import { makeStyles } from '#mui/styles'
import Modal from '#mui/material/Modal'
import FoodCard from '../src/ui/FoodCard.js'
import firebase from '../firebase/initFirebase'
import Box from '#mui/material/Box'
import Typography from '#mui/material/Typography'
const useStyles = makeStyles(theme => ({
mainContainer: {
}
}))
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'red',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
export default function CookBook() {
const router = useRouter();
const { id } = router.query;
const classes = useStyles()
const theme = useTheme()
const [loading, setLoading] = useState(true);
const [recipes, setRecipes] = useState([]);
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
useEffect(() => {
const getRecipesFromFirebase = [];
const subscriber = firebase.firestore()
.collection("recipes")
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(querySnapshot)
getRecipesFromFirebase.push({
...doc.data(), //spread operator
key: doc.id, // `id` given to us by Firebase
});
});
setRecipes(getRecipesFromFirebase);
console.log(recipes);
setLoading(false);
});
return () => subscriber();
}, [loading, router.events]); // empty dependencies array => useEffect only called once
if (loading) {
return (
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography id="modal-modal-title" variant="h6" component="h2">
Loading Data
</Typography>
</Box>
</Modal>)
}
return (
<FoodCard recipes={recipes} />
)
}
_app.js
import * as React from 'react';
import PropTypes from 'prop-types';
import Head from 'next/head';
import { ThemeProvider } from '#mui/material/styles';
import CssBaseline from '#mui/material/CssBaseline';
import { CacheProvider } from '#emotion/react';
import theme from '../src//ui/theme';
import createEmotionCache from '../src/createEmotionCache';
import Header from '../src/ui/Header';
import Grid from '#mui/material/Grid'
import Typography from '#mui/material/Typography'
import { Link as MUILink } from '#mui/material/'
import NextLink from 'next/link'
// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();
export default function MyApp(props) {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
return (
<CacheProvider value={emotionCache}>
<Head>
<meta name="viewport" content="initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
<Header />
<Grid container justify="center" alignItems="center" direction="column" >
<Grid item>
<NextLink href="/" passHref>
<MUILink underline="none" color="secondary" variant="h1">
Welcome To the Family Heirloom
</MUILink>
</NextLink>
</Grid>
</Grid>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
);
}
MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
emotionCache: PropTypes.object,
pageProps: PropTypes.object.isRequired,
};
Breakfast.js
import { React, useEffect, useState } from 'react'
import FilterMains from '../../src/ui/FilterMains'
import { useRouter } from 'next/dist/client/router'
import FoodCard from '../../src/ui/FoodCard'
import {
getFirestore, collection, query, where, onSnapshot
} from 'firebase/firestore'
const Breakfast = () => {
const router = useRouter();
const { id } = router.query;
const [breakfastloading, setBreakfastLoading] = useState(true);
const [breakfastRecipe, setBreakfastRecipe] = useState([]);
const db = getFirestore()
const docRef = collection(db, 'recipes')
//Query
const q = query(docRef, where("category", "==", 'Breakfast'))
useEffect(() => {
const getBreakfastFromFirebase = [];
onSnapshot(q, (snapshot) => {
snapshot.docs.forEach((doc) => {
getBreakfastFromFirebase.push({ ...doc.data() })
})
setBreakfastRecipe(getBreakfastFromFirebase)
setBreakfastLoading(false)
console.log(breakfastRecipe)
})
}, [breakfastloading, router.events]);
if (breakfastloading) {
return (
<h2>Loading Data</h2>
)
}
return (
<FoodCard recipes={breakfastRecipe} />
// <FoodCard recipes={recipes} />
)
}
export default Breakfast
FoodCard.js
import React from 'react'
import Card from '#mui/material/Card'
import CardHeader from '#mui/material/CardHeader';
import CardMedia from '#mui/material/CardMedia';
import Grid from '#mui/material/Grid'
import Container from '#mui/material/Container';
import Link from 'next/link'
import CardActionArea from '#mui/material/CardActionArea';
function FoodCard(props) {
return (
<div>
<Container>
< Grid container justify="center" alignItems="center" direction="row" >
<Grid container spacing={2}>
{props.recipes.map((recipe) => (
<Link href={`/recipes/${recipe.key}`} passHref>
<Grid key={recipe.id} item xs={12} md={6}>
<Card elevation={3} sx={{ maxWidth: 400 }}>
<CardActionArea>
<CardHeader
titleTypographyProps={{ fontWeight: "Bold" }}
title={recipe.title}
subheader={recipe.description}
/>
<CardMedia
component="img"
height="194"
image="/assets/comingsoon.jpg"
alt="Mac and Cheese"
/>
</CardActionArea>
</Card>
</Grid>
</Link>
))}
</Grid>
</Grid >
</Container>
</div>
)
}
export default FoodCard
Cracked it, although i can't fully explain why. In my learning i used two different methods for interfacing with firebase. The first method on the index page was what i would call the older way. The second method i used was in all of my other files, specific to interfacing with Version 9 of Firebase.
from this:
const subscriber = firebase.firestore()
.collection("recipes")
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(querySnapshot)
getRecipesFromFirebase.push({
...doc.data(), //spread operator
key: doc.id, // `id` given to us by Firebase
});
});
to this:
let getRecipesFromFirebase = [];
getDocs(colRef)
.then((snapshot) => {
let getRecipesFromFirebase = [];
snapshot.docs.forEach((doc) => {
getRecipesFromFirebase.push({ ...doc.data(), key: doc.id })
})
setRecipes(getRecipesFromFirebase);
console.log(recipes);
setLoading(false);
})
.catch(error => {
console.log(error.message)
})
Would love somebody more knowledgable (3 months into React) than myself to tell me as to why though.

Material UI CardMedia is not showing images

I'm using CardMedia to display images on the screen but it is not showing it.
I have gone through the similar question on Stack Overflow. There the solution give is to import the image and then use it. But here I'm fetching image url from backend using API call.
I have also tried changng heights but it didn't work.
Can anyone explain what is the issue?
import React from "react";
import axios from "axios";
import Card from "#material-ui/core/Card";
import CardHeader from "#material-ui/core/CardHeader";
import CardMedia from "#material-ui/core/CardMedia";
import CardContent from "#material-ui/core/CardContent";
import CircularProgress from "#material-ui/core/CircularProgress";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
root: {
maxWidth: 345,
},
media: {
height: 0,
paddingTop: "56.25%",
},
}));
function ProductList(props) {
const [productList, setProductList] = React.useState([]);
const [loading, setLoading] = React.useState(true);
const classes = useStyles();
const url = `https://api.growcify.com/dev/product/list/${props.listId}`;
const getList = () => {
axios.get(url).then((res) => {
console.log(res.data);
setProductList(res.data);
setLoading(false);
});
};
React.useEffect(() => {
getList();
}, []);
return !loading ? (
productList.length > 0 ? (
productList.map((list) => (
<Card className={classes.root}>
<CardContent>{list.name}</CardContent>
{list.photos.map((img) => {
img !== null && (
<Card className={classes.root}>
{console.log(img)}
<CardMedia
image={img}
component="img"
title="Some title"
className={classes.media}
/>
</Card>
);
})}
</Card>
))
) : (
<h2>No Data Available </h2>
)
) : (
<CircularProgress />
);
}
export default ProductList;
In the provided Screenshot, You can see in the console that I'm getting image url but image is not showing there.
Remove height: 0, from media style
image={"http://localhost:3000/yourPath/"+this.state.your-data.your-variable name}
Dont forget to padding top
Tell me if it works :D

react virtualized auto sizer does not work

I have been trying this code and it just does not work.
With AutoSizer, Row does not gets rendered.
It only starts working when I remove AutoSizer from the code.
I don't know what is wrong with the code and the docs is not helping either.
Full code:
import React, { Component } from 'react';
import Card from './Card';
import { FixedSizeList as List } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import memoize from "memoize-one";
const CARD_SIZE = 340;
class CardList extends Component {
getItemData = memoize((itemsPerRow, locations) => ({
itemsPerRow,
locations
}))
render() {
const { locations } = this.props;
console.log(locations.length)
const Row = ({ data, index, style }) => {
const { itemsPerRow, locations } = data;
console.log(data)
const items = [];
const fromIndex = index * itemsPerRow;
const toIndex = Math.min(fromIndex + itemsPerRow, locations.length);
for (let i = fromIndex; i < toIndex; i++) {
items.push(
<Card key={i} location={locations[i]} />
);
}
return (
<div className={'flex-auto'} style={style}>
{items}
</div>
);
}
return (
<div style={{ marginTop: "10px", height: "80%" }}>
<AutoSizer>
{
({ height, width }) => {
const itemsPerRow = Math.floor(width / CARD_SIZE) || 1;
const rowCount = Math.ceil(locations.length / itemsPerRow);
const itemData = this.getItemData(itemsPerRow, locations);
return (
<div>
<List
height={height}
itemCount={rowCount}
itemData={itemData}
itemSize={CARD_SIZE}
width={width}
>
{ Row }
</List>
</div>
);
}
}
</AutoSizer>
</div>
);
}
}
P.S. locations props is an array of images
I tried removing "react-virtualized-auto-sizer" and installed "react-virtualized"
Then,
import {AutoSizer} from 'react-virtualized';
and it works!!
But I don't want to keep react-window and react-virtualized together.
I hope the author of this package will help in fixing this problem.
Maybe it's because of height incompatibility. You can check with :
<div style={{ flex: '1 1 auto' , height: '100vh'}}>
<AutoSizer>
{({ height, width }) => {
return (
<FixedSizeList
className="List"
height={height}
itemCount={1000}
itemSize={35}
width={width}
>
{Row}
</FixedSizeList>
)
}}
</AutoSizer>
</div>

Categories