React Virtualized use recomputeRowHeights Method in functional components - javascript

Might be a silly question but I am trying to use the public method recomputeRowHeights specified in the documentation in a functional component while the entire react-virtualized library is based on class components.
My code for using it will be the following. It recomputes all row heights after window resizes.
window.addEventListener('resize', function(event) {
recomputeRowHeights();
console.log("recomputed!")
}, true);
Here is my wrapper for the List component:
import React from "react";
import { useState, useRef } from "react";
import { InfiniteLoader, List, WindowScroller } from 'react-virtualized';
import AutoSizer from 'react-virtualized-auto-sizer';
import Loader from "../components/decroations/Loader";
import TopMargin from "../components/decroations/TopMargin";
export default function InfiniteScroll({ getNextList, preRenderRadius }) {
const [hasNextPage, setHasNextPage] = useState(true);
const [isNextPageLoading, setIsNextPageLoading] = useState(false);
const [list, setList] = useState([{Component: <TopMargin />, height: 0.05}]);
const rowCount = hasNextPage ? list.length + 1 : list.length;
const loadMoreRows = isNextPageLoading ? () => {} : loadNextPage;
const isRowLoaded = ({index}) => !hasNextPage || index < list.length;
function rowheight({ index }){
if (!isRowLoaded({index})){
return 0.8*window.innerWidth;
}else{
return list[index].height*window.innerWidth;
}
}
const Item = ({index, key, style}) => {
if (!isRowLoaded({index})) {
return (
<div key={key} style={style} className='loaderWrapper'>
<Loader/>
</div>
)
} else {
return(
<div key={key} style={style}>
{list[index].Component}
</div>
)
};
}
function loadNextPage(){
setIsNextPageLoading(true);
getNextList(list.length - 1).then((newRows) => {
setList(list.concat(newRows))
setIsNextPageLoading(false)
}).catch(()=>{
setHasNextPage(false)
})}
return (
<InfiniteLoader
isRowLoaded={isRowLoaded}
loadMoreRows={loadMoreRows}
rowCount={rowCount}
threshold={preRenderRadius}
>
{({ onRowsRendered, registerChild }) => (
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer disableHeight>
{({width}) => (
<List
autoHeight
height={height}
onRowsRendered={onRowsRendered}
ref={registerChild}
rowCount={rowCount}
rowHeight={rowheight}
rowRenderer={Item}
width={width}
isScrolling={isScrolling}
scrollTop={scrollTop}
/>
)}
</AutoSizer>)}
</WindowScroller>
)}
</InfiniteLoader>
);
}

Related

How access a prop from layout in next js pages

I have this layout component , I am trying to pass a prop from layout to its children.
const Layout = ({ children }: Props) => {
const [open, setOpen] = useState(false);
const [selected, setSelected] = useState(countries[0]);
console.log("The country idsz ...........",selected.id)
const country= selected.id
const modifiedChildren = React.Children.map(children, child => {
if (React.isValidElement(child)) {
//#ts-ignore
return React.cloneElement(child, { testProp : country });
}
return child;
});
return (
<>
<LayoutContent sidebar={open} countriesWithsrc ={countriesWithsrc} selected={selected} lected={setSelected} >
{modifiedChildren}
</LayoutContent>
</>
)
}
export default Layout;
How can I access the modifiedChildren in pages? An example is the following page.
const ComingSoonCarbon = () => {
return (
<Layout>
<div className="flex justify-center font-bold mt-80 text-xl text-[rgb(245,132,38,0.93)]">
<h1>Development ongoing.Coming soon #2022</h1>
</div>
</Layout>
)
}
export default ComingSoonCarbon

react-transition-group does not animate

I use React and tranct-transition-group to write carousel components
But I encountered the problem that the animation does not take effect. The code is as follows
Link https://stackblitz.com/edit/react-ts-mi8mwj?file=Carousel.tsx
Carousel.tsx
import React, { FC, Fragment, ReactNode, useMemo, useState } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import CarouselItem, { ItemProps } from './CarouselItem';
import './Carousel.scss';
export interface Props {}
const Component: FC<Props> = (props) => {
const { children } = props;
const [curIndex, setCurIndex] = useState(1);
const length = useMemo(() => {
return Array.from(children as ReactNode[]).length;
}, [children]);
const onNext = () => {
setCurIndex((curIndex + 1 + length) % length);
};
const onPrev = () => {
setCurIndex((curIndex - 1 + length) % length);
};
return (
<Fragment>
<button onClick={onPrev}>prev</button>
<button onClick={onNext}>next</button>
<div className="g-carousel-wrapper">
<div className="g-carousel-window">
<TransitionGroup className="item">
{React.Children.map(children, (child, index) => {
const childElement = child as FC<ItemProps>;
if(child.type !== CarouselItem) throw new Error('必须是Item')
return (
<CSSTransition classNames="item" timeout={300} key={index}>
{React.cloneElement(childElement, {
index,
style: { display: curIndex !== index && 'none' },
})}
</CSSTransition>
);
})}
</TransitionGroup>
</div>
</div>
</Fragment>
);
};
type CarouselType = {
Item: FC<ItemProps>;
} & FC<Props>;
const Carousel: CarouselType = Component as CarouselType;
Carousel.Item = CarouselItem;
export default Carousel;
CarouselItem.tsx
import React, { CSSProperties, FC } from 'react';
export interface ItemProps {
index?: number;
style?: CSSProperties;
}
const carouselItem: FC<ItemProps> = (props) => {
const { children, style } = props;
return (
<div className="g-carousel-item" style={style}>
{children}
</div>
);
};
export default carouselItem;
I don't understand why not only there is no animation effect but also the className of CSSTransition does not exist, it seems that react-transition-group does not take effect thanks
I think we don't need to use the TransitionGroup component. CSSTransition itself supports a in prop, we can use this prop to control it's visibility.
So first, Add the in condition to the CSSTransition:
<CSSTransition
in={curIndex === index}
classNames="item"
timeout={300}
key={index}
>
And then, just remove the TransitionGroup:
<div className="g-carousel-wrapper">
<div className="g-carousel-window">
{React.Children.map(children, (child, index) => {
const childElement = child as FC<ItemProps>;
if (child.type !== CarouselItem) throw new Error('必须是Item');
return (
<CSSTransition
in={curIndex === index}
classNames="item"
timeout={300}
key={index}
>
{React.cloneElement(childElement, {
index,
style: { display: curIndex !== index && 'none' },
})}
</CSSTransition>
);
})}
</div>
</div>
It should be working now: https://stackblitz.com/edit/react-ts-kqed2n?file=Carousel.tsx

The initial render of the useEffect is disturbing the values of the cryptos. What is the solution?

I want that the Input field acts as a search bar and shows me the typed cryptocurrencies but because of the initial render of the useEffect, the value of the cryptos is being set to undefined and because of this no crypto is being shown on the page. Please suggest any alternate way to implement this functionality and also How can I stop the useEffect to get render at the starting .
import { Card, Row, Col, Input, Typography } from 'antd'
import React from 'react'
import { Link } from 'react-router-dom'
import { useGetCryptosQuery } from '../services/CryptoApi'
import { useState, useEffect } from 'react'
import millify from 'millify'
import { filter } from 'htmlparser2/node_modules/domutils'
const Cryptocurrencies = ( props ) => {
const count = props.simplified ? 10 : 100;
const { data: cryptoList, isFetching } = useGetCryptosQuery( count )
const [cryptos, setCryptos] = useState( cryptoList?.data?.coins )
const [searchTerm, setSearchTerm] = useState( '' )
useEffect( () => {
const filteredData = cryptoList?.data?.coins.filter( ( coin ) => { coin.name.toLowerCase().includes( searchTerm.toLowerCase() ) } )
setCryptos( filteredData )
}, [searchTerm, cryptoList] )
console.log( cryptos )
if ( isFetching ) {
return "loading...";
}
return (
<div>
{
<>
<div>
<Input placeholder="Search Cryptocurrency" onChange={( e ) => setSearchTerm( e.target.value )} className="" />
</div>
<Row gutter={[32, 32]} className="crypto-card-container">
{cryptos?.map( ( currency ) => {
return (
<Col xs={24} sm={12} lg={6} className="crypto-card" key={currency.id}>
<Link to={`/ crypto / ${currency.id} `}>
<Card
title={`${currency.rank}.${currency.name} `}
extra={<img className="crypto-image" src={currency.iconUrl} />}
hoverable
>
<p>Price : ${millify( currency.price )}</p>
<p>Market Cap : {millify( currency.marketCap )}</p>
<p>Daily Change : {millify( currency.change )}%</p>
</Card>
</Link>
</Col>
)
} )}
</Row>
</>
}
</div>
)
}
export default Cryptocurrencies
Simply can add a conditional if statement to solve the issue.
Let's take a look at the useEffect, As you describe at the component did mount, the searchTerm is still an empty string. By adding a simple if statement to check the seatchTerm value, filteredData and setCryptos methods only works when searchTerm has a string with a minimum of one letter.
useEffect( () => {
if(searchTerm.length > 0) {
const filteredData = cryptoList?.data?.coins.filter((coin) => { coin.name.toLowerCase().includes(searchTerm.toLowerCase())})
setCryptos(filteredData)
}
}, [searchTerm, cryptoList] )

"Warning: Encountered two children with the same key" for infinite scroll

I've implemented infinite scroll in React app, however, I get Warning: Encountered two children with the same key <...>. Keys should be unique so that components maintain their identity across updates. <...>.
If I replace key={movie.id} by key={i}, infinite scroll stops working.
This is the code of my component:
import{ useContext, useRef, useEffect } from "react";
import { Card, Grid, CardActionArea, CardMedia } from '#material-ui/core';
import FavoriteBorderIcon from '#material-ui/icons/FavoriteBorder';
import { MoviesContext } from "../../services/context";
import { Movie } from "../../services/movies.service";
import '../../App.scss';
import './Catalog.scss';
import noImage from '../../images/no-image-available.png';
import loadingSpinner from '../../images/loading-spinner.gif';
import { NavLink } from 'react-router-dom';
import useIntersectionObserver from '../../services/useIntersectionObserver';
import { fetchMovies } from "../../services/movies.service";
const posterBaseUrl = "https://image.tmdb.org/t/p/w300";
const CatalogCards = () => {
const { movies, updateMovies, searchedMovie, moviesPage, setMoviesPage, setSelectedMovie, setIsMoviePageFirstTimeOpened } = useContext(MoviesContext);
const loadingRef = useRef<HTMLDivElement | null>(null);
const entry = useIntersectionObserver(loadingRef, {})
const isVisible = !!entry?.isIntersecting;
const SetSelectedMovieId = (id: number) => {
setIsMoviePageFirstTimeOpened(true);
setSelectedMovie(id);
}
useEffect (
() => {
if ( isVisible ) {
setMoviesPage(moviesPage+1);
fetchMovies(String(moviesPage))
.then(nextPage => {
updateMovies((movies: Movie[]) => movies.concat(nextPage));
})
.catch(() => updateMovies([]))
}
},
[isVisible]
);
return (
<div >
<Grid container spacing={1} className="container-content">
{
movies.length > 0
?
movies.map((movie, i) => (
<Grid item key={movie.id}>
<NavLink to={'/movie/' + movie.id}>
<Card className="card-list" onClick={() => SetSelectedMovieId(movie.id)} >
<CardActionArea>
<CardMedia
component="img"
alt={"Poster of " + movie.title}
image={movie.poster_path ? posterBaseUrl + movie.poster_path : noImage}
title={movie.title}
/>
</CardActionArea>
</Card>
</NavLink>
</Grid>
))
:
searchedMovie ?
<div className="">Try a different phrase...</div>
:
<CardMedia
component="img"
image={loadingSpinner}
className="loading-spinner"
/>
}
</Grid>
<div ref={loadingRef}>...</div>
</div>
);
}
export default CatalogCards;
fetchMovies() method fetches info about movies from an API.
useIntersectionObserver is a custom hook that helps to check if ref={loadingRef} appears in the screen and more movies should be fetched.
How can I solve it?
Thanks!

Add items to Cart using React Context

I am new with React, I am trying to do the cart of the app that i am doing and I am having problems sending the items to the cart avoiding to have them duplicate or adding another extra item in the cart. These is the CartProvider code:
import { useState } from "react";
import CartContext from "./CartContext";
export const CartProvider = ({ children }) => {
const [list, setList] = useState();
const addCart = (varietals, quantity) => {
if (isInCart(varietals.item.id ) === -1){
setList(varietals)
}else{
list[isInCart].quantity += quantity
}
};
const isInCart = (id) => {
return list.findIndex(varietals => varietals.id === id)
};
return(
<>
<CartContext.Provider value={{list, addCart}}>
{children}
</CartContext.Provider>
</>);
};
This is the ItemDetail code which i use to display the Item:
import { useContext, useState } from "react";
import { Button, Container } from "react-bootstrap";
import { Link } from "react-router-dom";
import CartContext from "../../Context/CartContext";
import Item from "../Item/Item";
import ItemCountComponent from "../ItemCount";
const ItemDetail = ({ varietals }) => {
const [checkout, setCheckout] = useState(false);
const { list, addCart } = useContext(CartContext);
const onAdd = (count) => {
console.log("Selected ", count);
setCheckout(true);
addCart({Item: varietals, Quantity: count});
};
console.log(list)
return (
<>
<br />
<Container className="py-5 px-5 shadow">
<Item varietals={varietals} />
<div className="ml-4 mr-3">
<div className="font-italic mb-4 text-center text-muted">{varietals.description}</div>
{checkout ? <Link to='/Cart'><Button variant="info">Checkout</Button></Link> : <ItemCountComponent className="d-flex justify-content-center" onAdd={onAdd} stock={5} initial={1} />}
</div>
</Container>
</>
)
};
export default ItemDetail;
This is the Item.jsx:
import { Card, Container } from "react-bootstrap";
import "./Style.scss";
const Item = ({ varietals }) => {
return (
<>
<Container className="px-1">
<Card className="clemmy">
<Card.Img variant="top" src={varietals.pictureUrl} className="shadow"/>
<Card.Body>
<Card.Title className="d-flex justify-content-center text-muted">
{varietals.title}
</Card.Title>
<Card.Subtitle className="d-flex justify-content-center text-muted">
${varietals.price}
</Card.Subtitle>
</Card.Body>
</Card>
</Container>
</>
)
};
export default Item;
This is the ItemListContainer.jsx:
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import aimara from "../../aimara";
import ItemList from "../../Components/ItemList/ItemList";
const ItemListContainer = () => {
const [varietals, setVarietals] = useState([])
const { categoryId } = useParams();
useEffect(() => {
const myPromise = new Promise((resolve, reject) => {
if (categoryId) {
const products = aimara.filter((producto) => {
return producto.category.toString() === categoryId;
});
resolve(products);
} else resolve(aimara);
});
myPromise.then((result) => setVarietals(result));
}, [categoryId]);
return <ItemList varietals={varietals} />
};
export default ItemListContainer;
The error that I am seeing is "TypeError: Cannot read property 'id' of undefined"
If there is someone that could help me would be much appreciate it. Also, in case you need some extra file let me know and I update the post as quickly as I can. Cheers

Categories

Resources