next.js “setState”未与UI同步

nhn9ugyo  于 2023-02-22  发布在  其他
关注(0)|答案(1)|浏览(218)

我正在做一个论坛一样的网站与组件显示不同的论坛与分页按钮显示下一页使用Next.js。
我目前的页面实现是,我将查询数据使用getServerSideProps为初始页面加载。之后,当用户点击Next page按钮显示下一页的论坛。
我想查询在getServerSideProps中调用的相同API,以获取下一页数据。
我设法让按钮工作。但问题是,由setPage设置的page的数量似乎与UI不同步。
这是密码:

import dayjs from 'dayjs'
import type { GetServerSidePropsResult } from 'next'
import Head from 'next/head'
import Link from 'next/link'
import { useState } from 'react'

import type { GetPostListRequestQueries } from '@/types/GetPostListRequestQueries'
import type { GetPostListResponseBody } from '@/types/GetPostListResponseBody'

export async function getServerSideProps (): Promise<GetServerSidePropsResult<{ announcements: GetPostListResponseBody }>> {
  const queries: GetPostListRequestQueries = {
    announcement: true,
    category_based_announcement: false,
    page: 1
  }

  try {
    const url = new URL('http://127.0.0.1:5155/forum/posts')
    const searchParams = new URLSearchParams(queries as Record<string, string>)
    url.search = searchParams.toString()

    const response = await fetch(url)
    const announcements: GetPostListResponseBody = await response.json()

    return {
      props: {
        announcements
      }
    }
  } catch (e) {
    const response: GetPostListResponseBody = {
      posts: []
    }

    return {
      props: {
        announcements: response
      }
    }
  }
}

function Forum ({ announcements }: { announcements: GetPostListResponseBody }): JSX.Element {
  const [page, setPage] = useState(1)
  const [pageAnnouncements, setPageAnnouncements] = useState(announcements)

  const fetchAnnouncements = async (): Promise<void> => {
    const queries: GetPostListRequestQueries = {
      announcement: true,
      category_based_announcement: false,
      page
    }

    try {
      const url = new URL('http://127.0.0.1:5155/forum/posts')
      const searchParams = new URLSearchParams(queries as Record<string, string>)
      url.search = searchParams.toString()

      const response = await fetch(url)
      const announcements: GetPostListResponseBody = await response.json()

      setPageAnnouncements(announcements)
    } catch (e) {
      const response: GetPostListResponseBody = {
        posts: []
      }

      setPageAnnouncements(response)
    }
  }

  async function goToPreviousPage (): Promise<void> {
    setPage(() => page - 1)
    await fetchAnnouncements()
  }

  async function goToNextPage (): Promise<void> {
    setPage(() => page + 1)
    await fetchAnnouncements()
  }

  return (
    <>
      <Head>
        <title>Forum • ger</title>
        <meta name="description" content="reg spelled backwards" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>
      <main className="container mx-auto">
        <h1 className="text-4xl text-current font-bold">Forum {page}</h1>
        <div className="flex flex-row justify-between">
          <h3 className="text-2xl text-current">Global announcements</h3>
          <div className="flex flex-row btn-group">
            <button className="btn" onClick={goToPreviousPage}>Previous page</button>
            <button className="btn" onClick={goToNextPage}>Next page</button>
          </div>
        </div>
        <div className="overflow-x-auto">
          <table className="table w-full">
            <thead>
              <tr>
                <td>Topic</td>
                <td>Replies</td>
                <td>Views</td>
                <td>Activity</td>
              </tr>
            </thead>
            <tbody>
              {
                pageAnnouncements.posts.map(a => {
                  return (
                    <tr key={a.id}>
                      <td>
                        <Link className="font-bold link link-hover" href={{ pathname: '/forum/posts/[postId]', query: { postId: a.id } }}>{a.name}</Link>
                        <div className="flex flex-row">
                          <Link className="text-sm opacity-75 link link-hover" href={{ pathname: '/forum/users/[username]', query: { username: a.username } }}>
                            {a.username}
                          </Link>
                          <p className="text-sm opacity-75">
                             &nbsp;•&nbsp;{dayjs(a.created_timestamp).format('MMMM D, YYYY HH:mm')}
                          </p>
                        </div>
                      </td>
                      <td>10</td>
                      <td>{a.view_count}</td>
                      <td>1h</td>
                    </tr>
                  )
                })
              }
            </tbody>
          </table>
        </div>

        <h3 className="text-2xl text-current font-bold">Trending</h3>
        <div className="overflow-x-auto">
          <table className="table w-full">
            <tbody>
              <tr>
                <td>Cy Ganderton</td>
                <td>Quality Control Specialist</td>
                <td>Blue</td>
              </tr>
            </tbody>
          </table>
        </div>
      </main>
    </>
  )
}

export default Forum

您可以从下面的gif中看到,单词"Forum"旁边的数字和查询时使用的数字始终相差1。
我是Nextjs的新手,不理解这种行为,谢谢你的帮助。

bf1o4zei

bf1o4zei1#

解释

调用setState不会立即更新相关的state。需要重新渲染。这就是setState的设计工作原理。正如文档所述:
set函数只为下一次渲染更新状态变量,如果你在调用set函数后读取状态变量,你仍然会得到调用前屏幕上的旧值。
goToPreviousPagegoToNextPage中,您将调用setPage并立即调用fetchAnnouncements,因此它将使用上一次渲染中的page值。

溶液

要使它在不做太多更改的情况下工作,可以将page作为参数传递给fetchAnnouncements,如下所示:

const fetchAnnouncements = async (page: number): Promise<void> => {
 // ...
}
async function goToPreviousPage (): Promise<void> {
  setPage(() => page - 1)
  await fetchAnnouncements(page - 1)
}

async function goToNextPage (): Promise<void> {
  setPage(() => page + 1)
  await fetchAnnouncements(page + 1)
}

相关问题