Does react-virtualized work inside a functional component? - react-virtualized

I am asking because the examples I've seen are all in es6 classes.
And: I refactored my es6 class to a functional component. Now no rows appear any more. And no error either. Seems like the row renderer simply renders no rows any more.

Yes, you can use function component with react-virtualized
Does it perform as well as class component? I have no idea, tell me in comment ;)
Example:
import React from 'react'
import {Grid, Typography} from '#material-ui/core'
import PackageItem from './PackageItem'
import {PackagesByCategory} from '../functions/src/types'
import {makeStyles} from '#material-ui/core/styles'
import {WindowScroller, AutoSizer, List, ListRowRenderer, CellMeasurer, CellMeasurerCache} from 'react-virtualized'
const useStyles = makeStyles(theme => ({
container: {
minHeight: "80vh",
width: "100%"
},
title: {
marginTop: 64,
},
}))
const cache = new CellMeasurerCache({
defaultHeight: 60,
fixedWidth: true
});
const PackageCategoriesList = ({packagesByCategories}: PackageCategoriesList) => {
const classes = useStyles()
if(!packagesByCategories) {
return <></>
}
const rowRender: ListRowRenderer = ({index, key, style, parent}) => {
const packageByCategory = packagesByCategories[index]
return <CellMeasurer
cache={cache}
columnIndex={0}
key={key}
overscanRowCount={10}
parent={parent}
rowIndex={index}
>
<Grid item key={key} style={style}>
<Typography variant="h1" className={classes.title}>
{packageByCategory.category.name}
</Typography>
</Grid>
</CellMeasurer>
}
return <div className={classes.container}>
<WindowScroller
scrollElement={window}>
{({height, isScrolling, registerChild, onChildScroll, scrollTop}) => (
<div className={classes.list}>
{console.log("re der list" , height, isScrolling, scrollTop)}
<AutoSizer disableHeight>
{({width}) => (
<div ref={registerChild}>
<List
autoHeight
height={height}
isScrolling={isScrolling}
onScroll={onChildScroll}
overscanRowCount={2}
rowCount={packagesByCategories.length}
rowHeight={cache.rowHeight}
rowRenderer={rowRender}
scrollTop={scrollTop}
width={width}
/>
</div>
)}
</AutoSizer>
</div>
)}
</WindowScroller>
</div>
}
export default PackageCategoriesList

Related

TypeError: changeChecked is not a function

I am trying to pass changeChecked as prop from parent to this child component to capture the input element id but I am getting this error. I have mentioned the child component and parent component. Please help me to solve this error.
Child Component
import { useState, useEffect } from 'react';
const CheckBox = ({ changeChecked, section }) => {
return (
<>
{section.options.map((option, optionIdx) => (
<div key={option.value} className="flex items-center">
<input
id={`filter-${section.id}-${optionIdx}`}
name={`${section.id}[]`}
defaultValue={option.value}
type="checkbox"
defaultChecked={option.checked}
onChange={() => changeChecked(option.id)}
className="h-4 w-4 border-gray-300 rounded text-indigo-600 focus:ring-indigo-500"
/>
<label
htmlFor={`filter-${section.id}-${optionIdx}`}
className="ml-3 lg:text-sm min-w-0 flex-1 text-gray-500"
>
{option.label}
</label>
</div>
))}
</>
)
}
export default CheckBox;
Parent Component
import CheckBox from "../pages/collections/checkbox"
export default function App() {
const filters = [
{
id: 'brand',
name: 'Brands',
options: [
{ value: 'Casio', label: 'Casio', checked: false },
{ value: 'Yamaha', label: 'Yamaha', checked: false },
],
},
]
const onChangeChecked = (id)=>{
console.log(id)
}
return (
<div className="App">
{filters.map((section) => (
<CheckBox section={section} changeChecked={onChangeChecked} />
))}
</div>
);
}
This is a full example of passing a function as a prop.
Don't forget to pass the props when you are using the Input component, or you will get the same error as above.
export default function App() {
const onHandleClick = (e)=>{
console.log(e.target.value)
}
return (
<div className="App">
<Input onHandleClick={onHandleClick}/>
</div>
);
}
function Input ({ onHandleClick }) {
return <input onChange={(e) => onHandleClick(e)}/>
}

How to test a button inside a Material UI Select using React-Testing-Library

I have a button named "Open dialog" inside Material-UI Select. If I click on it, a dialog will open. I need to write test cases for that button which is inside Select. Can someone explain me how to do it. I am using snapshot testing and this is the code. The test case is failing as "unable to find data-testid openDialog"
main code:
import * as React from 'react';
import Box from '#mui/material/Box';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select, { SelectChangeEvent } from '#mui/material/Select';
import { Button, Dialog, DialogActions, Typography } from '#mui/material';
export default function BasicSelect() {
const [age, setAge] = React.useState('');
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value as string);
};
const [openDialog, setOpenDialog] = React.useState(false)
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
label="Age"
onChange={handleChange}
inputProps={{'data-testid': 'selectForm'}}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
<div style={{bottom: 0}}>
<Button variant="contained" onClick={() => setOpenDialog(true)} data-testid="openDialog">open dialog</Button>
</div>
</Select>
</FormControl>
<Dialog open={openDialog} maxWidth={'sm'}>
<Typography>hii all</Typography>
<DialogActions>
<Button onClick={() => setOpenDialog(false)} data-testid="cancel">Cancel</Button>
</DialogActions>
</Dialog>
</Box>
);
}
This is my test code:
import { render, fireEvent, screen, cleanup, waitFor } from '#testing-library/react';
and the test case is
it('Open dialog', async() =>
{
const { container } = render(
<ThemeProvider>
<Demo />
</ThemeProvider>
);
const selectForm = screen.getByTestId('selectForm')
fireEvent.click(selectForm);
const openDialog = await waitFor(() => screen.getByTestId('openDialog'));
fireEvent.click(openDialog);
expect(container).toMatchSnapshot();
});
Thank you.

React - trying to save fetched data in a variable but the variable returns empty

I fetched json data with async await and i wanted to save the fetched data in a variable in order to be able to use it with a map in my component,
the data comes in properly inside the function - i checked with an alert , and also in the variable inside the function it does display all the data , but somehow the variable outside the function returns empty .
here is some code:
both alerts in the following code return the right data.
export let fetchPosts = [];
export async function FetchPosts() {
await axios.get('https://jsonplaceholder.typicode.com/posts').then(
res => {
alert(JSON.stringify(res.data))
fetchPosts = JSON.stringify(res.data);
alert(fetchPosts)
}
).catch(err => {
alert('err');
})
}
import { fetchPosts } from '../services/post';
import { FetchPosts } from '../services/post';
export default function Posts() {
function clickme() {
FetchPosts()
}
return (<>
<button onClick={clickme}>Click me</button>
{fetchPosts.map((post, index) => (
<div key={post.id} className="card" style={{ 'width': '16rem', 'display': 'inline-block', 'margin': '5px' }}>
<div className="card-body">
<h6 className="title">{post.title}</h6>
<p className="card-text">{post.body}</p>
</div>
</div>
))}
</>)
}
State is the issue
React doesn't automatically reload on your singleton fetchPosts.
Instead, try...
export function FetchPosts() {
return axios.get('https://jsonplaceholder.typicode.com/posts');
}
then
import { useState } from 'react';
import { FetchPosts } from '../services/post';
export default function Posts() {
const [posts, setPosts] = useState([]);
function clickme() {
FetchPosts().then(res => {
setPosts(res.data);
});
}
return (<>
<button onClick={clickme}>Click me</button>
{posts.map((post, index) => (
<div key={post.id} className="card" style={{ width: '16rem', display: 'inline-block', margin: '5px' }}>
<div className="card-body">
<h6 className="title">{post.title}</h6>
<p className="card-text">{post.body}</p>
</div>
</div>
))}
</>)
}
https://codesandbox.io/s/jolly-almeida-q4331?fontsize=14&hidenavigation=1&theme=dark
If you want global state, that's another topic you should dive into entirely but you can do it with a singleton, you just need to incorporate it with hooks and an event emitter. I have a bit of a hacked version of this here https://codesandbox.io/s/react-typescript-playground-forked-h8rpu but you should probably stick to redux or mobx or AppContext which is more of a popular pattern.

How to get url with an ID using useEffect in Reactjs

I am working on a Mernstack application, in my application I have and Event Model and I want to show a single event when I click the event as a link.
the challenge that I am facing now is how to load a single event API with useEffect. it is working when I use componentDidMount but now I changed my application to use useEffect. with ComponentDidMount I used this.props.match.params.id to get the ID, but now that I am using useEffect, I don't know how to get the ID so that I can display a single event with the details and comments that are associated to it.
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import AddComingWithModal from "../components/coming-with-modal.component";
import { makeStyles } from "#material-ui/core/styles";
import clsx from "clsx";
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 CardActions from "#material-ui/core/CardActions";
import Collapse from "#material-ui/core/Collapse";
import Avatar from "#material-ui/core/Avatar";
import IconButton from "#material-ui/core/IconButton";
import Typography from "#material-ui/core/Typography";
import { red } from "#material-ui/core/colors";
import FavoriteIcon from "#material-ui/icons/Favorite";
import ShareIcon from "#material-ui/icons/Share";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import MoreVertIcon from "#material-ui/icons/MoreVert";
import { useTheme } from "#material-ui/core/styles";
import useMediaQuery from "#material-ui/core/useMediaQuery";
export default function EventAndComments() {
const theme = useTheme();
const [tileData, setTileData] = useState([]);
const useStyles = makeStyles((theme) => ({
root: {
maxWidth: 345,
},
media: {
height: 0,
paddingTop: "56.25%", // 16:9
},
expand: {
transform: "rotate(0deg)",
marginLeft: "auto",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shortest,
}),
},
expandOpen: {
transform: "rotate(180deg)",
},
avatar: {
backgroundColor: red[500],
},
}));
const classes = useStyles();
const [expanded, setExpanded] = React.useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
useEffect((id) => {
axios
.get(
"http://localhost:9000/events/" +
this.props.match.params.id +
"/eventcomments"
)
.then((response) => {
setTileData([...response.data]);
})
.catch(function (error) {
console.log(error);
});
}, []);
return (
<Card className={classes.root}>
<CardHeader
avatar={
<Avatar aria-label="recipe" className={classes.avatar}>
R
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={tileData.title}
subheader="September 14, 2016"
/>
<CardMedia
className={classes.media}
image="/static/images/cards/paella.jpg"
title="Paella dish"
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
This impressive paella is a perfect party dish and a fun meal to cook
together with your guests. Add 1 cup of frozen peas along with the
mussels, if you like.
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<FavoriteIcon />
</IconButton>
<IconButton aria-label="share">
<ShareIcon />
</IconButton>
<IconButton
className={clsx(classes.expand, {
[classes.expandOpen]: expanded,
})}
onClick={handleExpandClick}
aria-expanded={expanded}
aria-label="show more"
>
<ExpandMoreIcon />
</IconButton>
</CardActions>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
<Typography paragraph>Method:</Typography>
<Typography paragraph>
Heat 1/2 cup of the broth in a pot until simmering, add saffron and
set aside for 10 minutes.
</Typography>
<Typography paragraph>
Heat oil in a (14- to 16-inch) paella pan or a large, deep skillet
</Typography>
<Typography paragraph>
Add rice and stir very gently to distribute. Top with artichokes and
</Typography>
<Typography>
Set aside off of the heat to let rest for 10 minutes, and then
serve.
</Typography>
</CardContent>
</Collapse>
</Card>
);
}
You can use useParams()
const { id } = useParams()
instead of this.props.match.params.id

React-Virtualized InfiniteLoader lags on passing props to rendered rows

I just got started with react-virtualized and setup an InfiniteLoader->AutoSizer->VirtualScroller. Unfortunately, the rows that are lazily loaded aren't getting their props immediately. They render as if the props were null or undefined. If I scroll on past them and then scroll back to them, they are rendered correctly. Also, other than the initial pre-fetched rows, if I scroll slowly, all new rows render okay. If I send the scroll wheel flying, however, it will "land" on a set of "sub-rendered" rows. Given the code below, what am I doing wrong?
import React, { PropTypes } from 'react'
import { stitch } from 'keo'
import CSSModules from 'react-css-modules'
import css from './component.scss'
import R from 'ramda'
import { Divider, Paper } from 'material-ui'
import { AutoSizer, InfiniteLoader, VirtualScroll } from 'react-virtualized'
import 'react-virtualized/styles.css'
import Row from 'components/Row'
let list = R.repeat({score: 100, volume: 999}, 10)
function isRowLoaded ({ index }) {
return !!list[index]
}
function loadMoreRows ({ startIndex, stopIndex }) {
// fake loading
list = R.insertAll(startIndex, R.repeat({score: 100, volume: 999}, stopIndex - startIndex), list)
return Promise.resolve()
}
const render = ({ props }) => (
<div>
<Paper zDepth={2}>
<Paper zDepth={2}>
</Paper>
<div>
<InfiniteLoader
isRowLoaded={isRowLoaded}
loadMoreRows={loadMoreRows}
rowCount={1000}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ height, width }) => (
<VirtualScroll
ref={registerChild}
width={width}
height={height}
rowCount={1000}
rowHeight={72}
onRowsRendered={onRowsRendered}
rowRenderer={
({ index }) =>
<div>
<Row {...list[index]} />
<Divider />
</div>
}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
</div>
</Paper>
</div>
)
export const View = CSSModules(stitch({ render }), css)

Resources