How is possible to combine InfiniteLoader with WindowScroller? - react-virtualized

How can I create an infinite scroll list but in a window scroller? (the same as Facebook timeline - Mock up)?
Below is the code that I have tried, but it does not work as expected. It only displays the first items and after that it does not display anything more.
<div className={styles.WindowScrollerWrapper}>
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={list.size}
threshold={10}>
{({ onRowsRendered, registerChild }) => (
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<List
ref={registerChild}
className={styles.List}
autoHeight
height={height}
width={width}
onRowsRendered={onRowsRendered}
rowCount={list.size}
rowHeight={30}
rowRenderer={this._rowRenderer}
scrollToIndex={randomScrollToIndex}
/>
)}
</AutoSizer>
)}
</WindowScroller>
)}
</InfiniteLoader>
</div>
Many thanks in advance.
Update
Here is the URL to demo: https://plnkr.co/edit/akyEpZ0cXhfs2jtZgQmN

Based on your Plnkr, here's a corrected Plnkr that shows how it should work. (You were forgetting to pass the scrollTop param from WindowScroller to the child List.)
Here you go:
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={list.size}
threshold={10}>
{({ onRowsRendered, registerChild }) => (
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<List
ref={registerChild}
className="List"
autoHeight
height={height}
width={width}
onRowsRendered={onRowsRendered}
rowCount={list.size}
rowHeight={30}
rowRenderer={this._rowRenderer}
scrollTop={scrollTop} />
)}
</AutoSizer>
)}
</WindowScroller>
)}
</InfiniteLoader>

Related

React Route to Page Using Unique ID

I have multiple rendered components on a page. When I click on one I want to navigate to a new page with all of the information specific to that component. What I need is:
OnClick -> Identify gameId variable
Using that gameID run a query against an API endpoint
Take the rendered data from the API and display it on a new page with route /nhl/:gameId
Example screenshot of /NHL page with different components.
/NHL Route
I have this existing Scores component:
function Scores(props) {
const navigate = useNavigate('/');
const handleClick = () => {
let path = '/' + {gameId};
navigate(path);
}
return (
<Card sx={{ Height: 50, padding: .25, margin: 3 }}>
<CardActionArea onClick={handleClick}>
<div className = "">
<CardContent sx={{ width: 200 }}>
<Stack direction="row" spacing={2}>
<Typography component="div" variant="h6">{props.gameStatus}</Typography>
<Typography>{props.gameDateandTime}</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.awayTeam}</Typography>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.awayPoints}</Typography>
</Stack>
<Typography>#</Typography>
<Stack direction="row" spacing={2}>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.homeTeam}</Typography>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.homePoints}</Typography>
</Stack>
</CardContent>
</div>
</CardActionArea>
</Card>
)
}
export default Scores;
The Scores.js component above is rendered in the NHL route from the screenshot:
function NHL(){
var date = year + "-" + month + "-" + day;
const [scores, setScores] = useState([{ }]);
useEffect(() => {
let url = "http://localhost:3001/schednhl";
fetch(url).then(res => {
if(res.ok) {
return res.json()
}
}).then(jsonRes => setScores(jsonRes));
}, []);
return (
<div className='Home'>
<Banner />
<Typography variant="h3" align="center" sx={{ paddingTop: 5 }}>Live Games</Typography>
<hr className="lineRow"></hr>
<Stack direction="row" spacing={2}>
<div className="scroll-box">
<div className="scroll-box-wrapper">
<div className="scroll-box-container" role="list">
{scores.map((score, index) => {
if(score.gameDate === date && score.gameStatus === "closed" || score.gameStatus === "inprogress")
return (
<Scores
className="scroll-box-item"
role="listitem"
key={index}
id={index}
gameDate={score.gameDate}
gameStatus={score.gameStatus === "closed" ? "Final" : "Live" }
homeTeam={score.homeTeam}
homePoints={score.homePoints}
awayPoints={score.awayPoints}
awayTeam={score.awayTeam}
/>
);
})}
</div>
</div>
</div>
</Stack>
<h3 className="home-header" style={{paddingTop: 10 }}>Upcoming Games</h3>
<hr className="lineRow" style={{ }}></hr>
<Grid container spacing={3} direction="row" justifyContent="flex-start">
{scores.map((score, index) => {
if(score.gameDate === date && score.gameStatus === "scheduled" || score.gameStatus === "upcoming")
return (
<Scores
className="scroll-box-item"
role="listitem"
key={index}
id={index}
time={score.gameDateandTime}
gameStatus={score.gameStatus === "scheduled" ? "Upcoming" : ""}
homeTeam={score.homeTeam}
homePoints={score.homePoints}
awayPoints={score.awayPoints}
awayTeam={score.awayTeam}
/>
);
})}
</Grid>
</div>
);
}
export default NHL;
How can I access the gameId from the NHL function and use that in my Score component?

React Auth0 (Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> )

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it. at withAuthenticationRequired
export const AdminRoute = ({ component: Component, navbar: Navbar, ...props }) => {
console.log('props==>', props.dynamicClass);
let dynamicClass = `wrapper ${props.dynamicClass}`
return (
<Route
{...props}
render={(props) => (
<MediaQuery minWidth={800}>
{(matches) => {
return (
<React.Fragment>
<WithAuthenticationRequired>
<div className={dynamicClass}>
<Navbar />
<Component {...props} />
<Footer />
</div>
</WithAuthenticationRequired>
</React.Fragment>
);
}}
</MediaQuery>
)}
/>
);
};
What you are passing into MediaQuery is a function definition (as state by the error).
The components you are returning will only be returned once this function is called. Following is one way to force the call(does not work with arrow functions):
<MediaQuery minWidth={800}>
{(function (matches) {
return (
<React.Fragment>
<WithAuthenticationRequired>
<div className={dynamicClass}>
<Navbar />
<Component {...props} />
<Footer />
</div>
</WithAuthenticationRequired>
</React.Fragment>
);
})()}
</MediaQuery>;
Or you can do something like creating a new function that returns the components you need, and then calling that function within <MediaQuery>:
<MediaQuery minWidth={800}>{getFragment(matches)}</MediaQuery>;
const getFragment = (matches) => {
return (
<React.Fragment>
<WithAuthenticationRequired>
<div className={dynamicClass}>
<Navbar />
<Component {...props} />
<Footer />
</div>
</WithAuthenticationRequired>
</React.Fragment>
);
};

React Bootstrap Card Result in Multiple Columns

Below is my code, my problem is, the results are displaying at the bottom one by one like this:
1
2
3
I want to display each output result as follows? as 3 column
1 2 3
4 5 so on......
I have used https://react-bootstrap.github.io/components/cards/ component to display the database output
{Mylist.length && Mylist.map((item, index) => {
return (
<Card key={index} style={{ width: '18rem' }}>
<Card.Body>
<Card.Subtitle className="mb-2 text-muted">{moment(item.date_time).format('LLL')}</Card.Subtitle>
<Card.Text>{item.content}</Card.Text>
<Button variant="danger" size="sm" data-id={item.id} onClick={() => remove(item.id)} >Delete</Button>{' '}
</Card.Body>
</Card>
)
})}
Try the following approach, see more at https://react-bootstrap.netlify.app/components/cards/#card-groups
<Row xs={1} md={2} className="g-3">
{Mylist.length && Mylist.map((item, index) => {
return (
<Col key={index}>
<Card style={{ width: '18rem' }}>
<Card.Body>
<Card.Subtitle className="mb-2 text-muted">{moment(item.date_time).format('LLL')}</Card.Subtitle>
<Card.Text>{item.content}</Card.Text>
<Button variant="danger" size="sm" data-id={item.id} onClick={() => remove(item.id)} >Delete</Button>{' '}
</Card.Body>
</Card>
</Col>
)
})}
</Row>

React-Virtualized keep floating Table header inside WindowScroller

The documentation for WindowScroller in React-Virtualized (https://github.com/bvaughn/react-virtualized/blob/master/docs/WindowScroller.md) suggests that it is compatible with the Table and the below code does sort of work
<WindowScroller>
{({ height, isScrolling, registerChild, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<div ref={registerChild}>
<Table
autoHeight
isScrolling={isScrolling}
scrollTop={scrollTop}
height={height}
width={Math.max(width, 700)}
headerHeight={20}
rowHeight={30}
rowCount={warps.length}
rowGetter={({ index }) => warps[index]}
>
<Column
dataKey="one"
label="One"
width={100}
/>
<Column
dataKey="two"
label="Two"
width={50}
/>
</Table>
</div>
)}
</AutoSizer>
but the table header is now no longer floating above the table as was the case without WindowScroller. Is there any way that I can get this behavior back? I guess I could manually render the header outside the table, but that will be quite a hassle because I want to use flexGrow on the columns so I will not know their widths and would probably need to create a clone table with no rows for that?

How to make Material-UI GridListTitleBar and Image a Link in ReactJS

I am using Material UI GridList to display a list of Events in React. Everything is working fine except that I am not able to make Title or Image a Link. Does anyone know how to make Title and Image a Link?
Here is my Component.
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
key={Math.floor(Math.random() * new Date().getTime())}
>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.description}</span>}
actionIcon={<IconButton title={tile.title} />}
/>
</GridListTile>
);
})}
</GridList>
</div>
);
Update
This is an update to the answer that was given below. The first image is now smaller than the rest of the images after update the code with the solution given below.
Here is the new code that I am trying:
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
key={Math.floor(Math.random() * new Date().getTime())}
>
<a href={"events/" + tile._id + "/eventcomments"}>
<img
src={tile.eventImage}
alt={tile.title}
className="MuiGridListTile-imgFullHeight"
/>
<GridListTileBar title={tile.title} />
</a>
</GridListTile>
);
})}
</GridList>
</div>
Try wrapping the image with an anchor tag like this:
// ...
<GridListTile key={Math.floor(Math.random() * new Date().getTime())}>
<a href="https://www.google.com/">
<img src={tile.eventImage} alt={tile.title} className="MuiGridListTile-imgFullHeight" />
</a>
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.description}</span>}
actionIcon={<IconButton title={tile.title} />}
/>
</GridListTile>
// ...
It is important, after wrapping the image inside an anchor tag to add the class MuiGridListTile-imgFullHeight to the image to keep the same styling of the grid. Normally this class is added automatically, but if you wrap it inside an anchor tag it isn't. So you need to add it manually.
Update
The image shows expected behavior, because your first image is not wide enough to cover the whole tile and you only added the class to scale the img to full height. There is also a class to scale to full width: MuiGridListTile-imgFullWidth, but you can't use both of these classes together for your use case as the styles conflict (see the style definition here: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/GridListTile/GridListTile.js).
You can try to set the width to 100% in the image style prop:
<img
// ...
style={{ width: "100%" }}
className="MuiGridListTile-imgFullHeight"
/>
I eventually managed to get it to work without breaking or stretching my images, and here is what I did.
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
component={Link}
to={"/events/" + tile._id + "/eventcomments"}
key={Math.floor(Math.random() * new Date().getTime())}
>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar title={tile.title} />
</GridListTile>
);
})}
</GridList>
</div>
const Gallery = () => {
const classes = useStyles();
return (
<Fragment>
<div style={{background: "black"}}>
<Toolbar/>
<Grid container spacing={0}>
<Grid item xs={12}>
<Paper className={classes.paper}>GALERIA</Paper>
<div className={classes.root}>
<GridList cellHeight={280} className={classes.gridList} cols={1}>
{tileData.map((tile) => (
<GridListTile style={{ width: "100%" }} key={tile.img} cols={tile.cols || 1}>
<img src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>
</div>
</Grid>
</Grid>
</div>
</Fragment>
)
}
export default Gallery;
You can change the type of underlying component rendered by the GridListTime Material-UI component. Just add the component="a" to have it render as an anchor tag. Then you may specify an href prop to pass in your route. See below:
<GridList cellHeight={160} className={classes.gridList} cols={3}>
{tileData.map((tile) => (
<GridListTile key={tile.img} cols={tile.cols || 1} component="a" href="/">
<img src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>

Resources