使用 SWR 条件获取和 react-bootstrap 手风琴

useSWR conditional fetch and react-boostrap Accordion

尝试将 youtube 评论加载到无限加载组件中(为此使用 npm)

由于无限加载组件是父 Accordion 组件的子组件(来自 react-bootstrap)以及我正在尝试的,所以发生了混乱仅当 Accordion 被点击(打开)时,achieve 才使用 useSWR 获取。

我尝试的是使用 useSWR 条件,以便仅在状态“show”为真时获取,这是在函数内部设置的:

const showComments = () => {
    setShow(true)
    if (comments) {
      setCommChunks(_.chunk(comments.comm, 10))
      setCommList(commChunks[counter])
    }
  }

调用了 Accordion.Toggle onClick 事件。

但是我点了两次手风琴之后才显示评论,这是为什么?

我的代码是:

import { useState, useEffect } from 'react'
import { Row, Col, Button, Accordion } from 'react-bootstrap'
import * as _ from 'lodash'
import useSWR from 'swr'
import { MdUnfoldMore } from 'react-icons/md'
import InfiniteScroll from "react-infinite-scroll-component"
import Comments from './Comments'

const siteurl = process.env.NEXT_PUBLIC_SITE_URL

export default function VideoComments({ video }){
    
  const [show, setShow] = useState(false)
  const [counter, setCounter] = useState(0)
  const [commList, setCommList] = useState(null)
  const [commChunks, setCommChunks] = useState([])

  const showComments = () => {
    setShow(true)
    if (comments) {
      setCommChunks(_.chunk(comments.comm, 10))
      setCommList(commChunks[counter])
    }
  }

  const fetcher = (...args) => fetch(...args).then(res => res.json())
  const { data: comments, error } = useSWR(show ? `${siteurl}/api/c/${video.id}` : null, fetcher)
  
  // useEffect(() => {
  //   if (comments) {
  //     commChunks = _.chunk(comments.comm, 10)
  //     setCommList(commChunks[counter])
  //   }
  // },[comments])

  const fetchMoreData = () => {
    const newCounter = counter + 1;

    // loaded all, return
    if (commChunks[newCounter] === undefined || commChunks[newCounter] == null) {
        return;
    }

    const newCommList = [
        ...commList,
        ...commChunks[newCounter]
    ]
    setCommList(newCommList)
    setCounter(newCounter)
  }

  return (
    <div>
      <Accordion>
        <Row>
          <Col xs={12}>
            <Accordion.Toggle as={Button} onClick={() => {showComments()}} variant="link" eventKey="0"><div><span>Comments</span></div></Accordion.Toggle>
          </Col>
        </Row>
        <Accordion.Collapse eventKey="0">
          <div id="commentsBox" style={{maxHeight: '300px', overflowY: 'auto'}}>
            <Col xs={12}>
              {commList &&
                <InfiniteScroll
                    dataLength={commList.length}
                    next={fetchMoreData}
                    hasMore={true}
                    scrollableTarget="commentsBox"
                >
                  <Comments data={commList} />
                </InfiniteScroll>
              }
            </Col>
          </div>
        </Accordion.Collapse>
      </Accordion>
    </div>
  );
}

编辑:按照下面的建议,我重新激活了useEffect,但它仍然需要点击两次手风琴

const showComments = () => {
    setShow(true)
    if (comments) {
      setCommChunks(_.chunk(comments.comm, 10))
      setCommList(commChunks[counter])
    }
  }

  const { data: comments } = useSWR(show ? `${siteurl}/api/c/${video.id}` : null, fetcher)

  useEffect(() => {
    if (comments) {
      setCommChunks(_.chunk(comments.comm, 10))
      setCommList(commChunks[counter])
    }
  },[comments])

您评论了处理评论的 useEffect :

  // useEffect(() => {
  //   if (comments) {
  //     commChunks = _.chunk(comments.comm, 10)
  //     setCommList(commChunks[counter])
  //   }
  // },[comments])

发生了什么:

  1. 您点击手风琴,showComments被调用
  2. show设置为真,但是因为comments未定义,所以commListcommChunks没有设置
  3. 组件重新渲染,现在useSWR可以使用url获取数据
  4. 组件在获取 si 完成后重新呈现,现在 comments 包含数据
  5. 您第二次点击手风琴,showComments被调用
  6. show设置为true,这次设置commListcommChunks
  7. 组件重新呈现 InfiniteScrollComments

问题出在您的 useEffect 中,在修改 commChunks 状态后立即调用 setCommList(commChunks[counter]) 不会有更新的值。在 React 中设置状态是一个异步操作(参见 )。

您应该将注释保存在块范围变量中,并使用它来连续更新两种状态。

useEffect(() => {
    if (comments) {
        const commentsChunks = _.chunk(comments.comm, 10)
        setCommChunks(commentsChunks)
        setCommList(commentsChunks[counter])
    }
}, [comments])