在用户向下滚动时获取更多内容

时间:2019-03-06 10:50:20

标签: reactjs

一旦用户点击提交结果,我就有一个带有搜索输入的页面。

会有很多结果,我不想一次全部加载,如何在鼠标移动时使用Lodash调节器将更多数据提取到页面中?

这是我的反应组件:

const getContacts = async (searchString) => {
  const { data: contactsInfo} = await axios.get(`api/Contats/Search?contactNum=${searchString}`);
  return contactsInfo;
};
export default class Home extends React.Component {
  state = {
    contactsInfo: [],
    searchString: '',
  };

  handleSubmit = async () => {
    const { searchString } = this.state;
    const contactsInfo = await getContacts(searchString);
    this.setState({ contactsInfo });
  };

  onInputChange = e => {
    this.setState({
      searchString: e.target.value,
    });
  };

  onMouseMove = e => {

  };

  render() {
    const { contactsInfo, searchString, } = this.state;
    return (
          <div css={bodyWrap} onMouseMove={e => this.onMouseMove(e)}>
            <Header appName="VERIFY" user={user} />
            {user.viewApp && (
              <div css={innerWrap}>
                <SearchInput
                  searchIcon
                  value={searchString || ''}
                  onChange={e => this.onInputChange(e)}
                  handleSubmit={this.handleSubmit}
                />
                     {contactsInfo.map(info => (
                      <SearchResultPanel
                        info={info}
                        isAdmin={user.isAdmin}
                        key={info.id}
                      />
                    ))}
              </div>
            )}
            <Footer />
          </div>

    );
  }
}

1 个答案:

答案 0 :(得分:0)

我认为,使用getContacts()可以检索所有联系人,然后只想以某种速率显示它们,例如显示前20个,然后当到达最后一个时,又显示20个。 只是问一下,因为这与“让我们先获取前20个联系人并显示它们,然后当用户到达最后一个联系人时再获取20s”完全不同。

因此,如果我做的第一个假设是正确的,我可以建议你使用 Intersection Observer API (交叉观察者API) https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

这在像您这样的情况下确实很有用(甚至在文档“滚动页面时延迟加载图像或其他内容”中编写。” )。

这个想法是,您应该添加此“相交观察器”,并在最后一张图像上开始观察:一旦最后一张图像出现在屏幕上,该观察器就会运行回调(您甚至可以决定必须在屏幕上)。 例如,您可以说,只要1px的图像出现在屏幕上,就添加另外20s张图像!

请注意,如果再次显示20s图像,则必须取消观察当前的观察图像,并观察新的最后一张图像!

我还建议不要将观察者放在最后一张图像上,而应该放在最后一张图像上。

编辑:我不确定这是否能回答您的问题。如果我考虑标题“在用户向下滚动时获取更多内容”,它确实可以,但是它实际上并没有使用鼠标悬停(即使我认为此实现是实现您目标的最佳选择)。

EDIT2 :事情已经过去了,我已经添加了小提琴,并且这里有一个代码笔:https://codepen.io/Gesma94/pen/OqpOQb

请注意,我已经用不同颜色的div模拟了联系人。发生的情况是,当屏幕上出现倒数第三个联系人(div)时,会在状态中添加新的联系人。现在,联系人只是空对象,但是您可以在fetchMoreContent()内运行访存或执行任何所需的操作。这足够清楚吗? :)我也评论了代码。

/* Just a function that create a random hex color. */
function randomColor() {
    let randomColor = '#';
    const letters = '0123456789ABCDEF';

    for (let i = 0; i < 6; i++) {
        randomColor += letters[Math.floor(Math.random() * 16)];   
    }
    
    return randomColor;
}

class Home extends React.Component {
    contactList = null; // Ref to the div containing the contacts.
    contactObserved = null; // The contact which is observed.
    intersectionObserver = null; // The intersectionObserver object.
    
    constructor(props) {
        super(props);
        
        this.contactList = React.createRef();
        this.state = {
            loading: true,
            contactsToShow: 0,
            contacts: []
        };
    }
    
    componentDidMount() {
        /* Perform fetch here. I'm faking a fetch using setTimeout(). */
        setTimeout(() => {
            const contacts = [];
            for (let i=0; i<100; i++) contacts.push({});
            
            this.setState({loading: false, contacts, contactsToShow: 10})}, 1500);
    }
    
    componentDidUpdate() {
        if (!this.state.loading) this.handleMoreContent();
    }
    
    render() {
        if (this.state.loading) {
            return <p>Loading..</p>
        }
        
        return (
            <div ref={this.contactList}>
                {this.state.contacts.map((contact, index) => {
                    if (index < this.state.contactsToShow) {
                        const color = contact.color || randomColor();
                        contact.color = color;                    

                        return (
                            <div
                                className="contact"
                                style={{background: color}}>
                                {color}
                            </div>
                        );
                    }
                    
                })}
            </div>
            
        );
    }

    handleMoreContent = () => {
        /* The third last contact is retrieved. */
        const contactsDOM = this.contactList.current.getElementsByClassName("contact");
        const thirdLastContact = contactsDOM[contactsDOM.length - 3];

        /* If the current third last contact is different from the current observed one,
         *  then the observation target must change. */
        if (thirdLastContact !== this.contactObserved) {
            /* In case there was a contact observed, we unobserve it and we disconnect the
             *  intersection observer. */
            if (this.intersectionObserver && this.contactObserved) {
                this.intersectionObserver.unobserve(this.contactObserved);
                this.intersectionObserver.disconnect();
            }
            
            /* We create a new intersection observer and we start observating the new third
             *  last contact. */
            this.intersectionObserver = new IntersectionObserver(this.loadMoreContent, {
                root: null,
                threshold: 0
            });
        
            this.intersectionObserver.observe(thirdLastContact);
            this.contactObserved = thirdLastContact;
        }
    }
    
    loadMoreContent = (entries) => {
         entries.forEach(entry => {
            if (entry.isIntersecting) {
                let contactsCounter = this.state.contacts.length;
                let contactsToShow = this.state.contactsToShow + 10;
        
                if (contactsToShow > contactsToShow) contactsToShow = contactsToShow;
        
                this.setState({contactsToShow});
            }
        })
        
    }
}

ReactDOM.render(<Home />, document.getElementById('root'));
@import url(https://fonts.googleapis.com/css?family=Montserrat);

body {
    font-family: 'Montserrat', sans-serif;
}

.contact {
    width: 200px;
    height: 100px;
    border: 1px solid rgba(0,0,0,0.1);
}

.contact + .contact {
    margin-top: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id='root'></div>