【问题标题】:Infinite Scroll using Algolia on React在 React 上使用 Algolia 的无限滚动
【发布时间】:2021-04-02 17:15:24
【问题描述】:

我正在尝试在我的 React 应用上实现无限滚动,以获取来自 Algolia 的搜索命中。

我在他们的文档中发现了一个类组件。我使用 React Hooks 所以试图让它在 React Hooks 上工作。我得到的只是这么多的渲染,当这个组件挂载时我的应用程序被挂断了。

这是我的代码:

import React, { useEffect, useRef } from 'react';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import Container from '@material-ui/core/Container';
import Grid from '@material-ui/core/Grid';
import { connectHits, connectInfiniteHits } from 'react-instantsearch-dom';
import noItemImage from './../../assets/img/noItemImage.png'
import { Link } from 'react-router-dom'
import ShareIcon from '@material-ui/icons/Share';
import PropTypes from 'prop-types';

function AlgoliaHits(props) {
    const { hits } = props
    console.log(hits)
    var sentinel = useRef(null)
    
    useEffect(() => {
        function onSentinelIntersection (entries){
            const { hasMore, refine } = props

            entries.forEach(entry => {
                if (entry.isIntersecting && hasMore) {
                    refine()
                }
            })
        }

        var observer = new IntersectionObserver(onSentinelIntersection, {})

        observer.observe(sentinel.current)
        return () => {
            observer.disconnect()
        }
    }, [props])

    return (
        <Container maxWidth="md" style={{ marginBottom: 100 }}>
            <Grid container spacing={2}>
                {
                    hits.map(hit => (
                        <Grid item xs={12} sm={6} md={4} lg={4} xl={3}>
                            <Link to={`/item/${hit.item_id}`} style={{ textDecoration: 'none' }}>
                                <Card maxWidth={210} key={hit.item_id} elevation={0}>
                                    <CardActionArea>
                                        <CardMedia
                                            component="img"
                                            alt="Contemplative Reptile"
                                            height="140"
                                            image={
                                                hit.item_images_url ? 
                                                    hit.item_images_url.length === 0 ?
                                                    noItemImage
                                                    :
                                                    hit.item_images_url[0]
                                                : 
                                                noItemImage
                                            }
                                            title={hit.item_name}
                                        />
                                        <CardContent>
                                            <Typography gutterBottom variant="h5" component="h2"
                                                style={{ whiteSpace: 'nowrap', width: 250, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                                                {hit.item_name}
                                            </Typography>
                                            <Typography variant="body2" color="textSecondary" component="p" 
                                                style={{ whiteSpace: 'nowrap', width: 200, overflow: 'hidden', textOverflow: 'ellipsis'  }}>
                                                {hit.item_description}
                                    </Typography>
                                        </CardContent>
                                    </CardActionArea>
                                    <CardActions>
                                        <Button size="small" color="primary" component={Link} to={`/item/${hit.item_id}`}>
                                            View
                                        </Button>
                                        <IconButton size="small" color="secondary">
                                            <ShareIcon style={{ padding: 4 }}/>
                                        </IconButton>
                                    </CardActions>
                                </Card>
                            </Link>
                        </Grid>                        
                    ))
                }
            </Grid>
            <div id="sentinel" ref={sentinel} />
        </Container>
    );
}

AlgoliaHits.propTypes = {
    hits: PropTypes.arrayOf(PropTypes.object).isRequired,
    hasMore: PropTypes.bool.isRequired,
    refine: PropTypes.func.isRequired,
}

const AlgoliaInfiniteScroll = connectHits(AlgoliaHits)

const ItemCard = connectInfiniteHits(AlgoliaInfiniteScroll)

export default ItemCard

还有here's,我在其中使用了参考。我在做什么错?又该如何解决?

TIA

【问题讨论】:

    标签: reactjs material-ui algolia react-functional-component intersection-observer


    【解决方案1】:

    我的第一个想法是问题可能出在useEffect 依赖数组中。

    useEffect 挂钩将在其依赖项之一发生更改时触发。在您的情况下,您已将依赖项数组指定为[props],这意味着每次道具更改时,它都会再次触发。由于props 可能经常更改,对于props.hits 等对象,我敢打赌这是将您的代码放入您提到的无限循环中。

    我相信在原始示例中,onSentinelIntersection 功能仅在组件挂载时出现一次。

    我会从一个空的依赖数组开始:[] 在你的 useEffect 的末尾。这是替换componentDidMount 的一种(虽然有点老套)方法,并且只在组件第一次渲染时运行一次。

    如果这有帮助,那么您可能已经成功了。但是,我还建议将 onSentinelIntersection 函数移出 useEffect (或者甚至完全移出组件,如果可能的话!) - 因为最好将尽可能多的代码保留在 useEffect 之外(如每次执行时都会对其进行评估)。

    比如:

    function AlgoliaHits(props) {
        const { hits } = props
        console.log(hits)
        var sentinel = useRef(null)
        
        function onSentinelIntersection (entries){
            const { hasMore, refine } = props
    
            entries.forEach(entry => {
                if (entry.isIntersecting && hasMore) {
                    refine()
                }
            })
        }
    
        useEffect(() => {
            var observer = new IntersectionObserver(onSentinelIntersection, {})
            observer.observe(sentinel.current)
    
            return () => observer.disconnect()
        }, [])
    
        ....
    

    可能是一个好的开始。您也可以安全地将 [IntersectionObserver, onSentinelIntersection] 添加到 useEffect 依赖项中,因为它们在技术上是 useEffect 起作用所必需的,并且在这种情况下不应导致任何进一步的 useEffect 触发器。

    React useEffect docsDependencies & Performance 部分在这里很有帮助 - 以及整个页面本身。

    对于额外的资源,我真的从 Dan Abramov 的 The Complete Guide To useEffect 中获得了很多收获 - 读了很久,但我认为值得(当你有时间的时候!)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-09
      • 2022-07-07
      • 2021-11-22
      • 1970-01-01
      • 2021-04-12
      • 1970-01-01
      • 2017-11-04
      • 2016-01-26
      相关资源
      最近更新 更多