javascript - `setState` 未与UI同步

标签 javascript reactjs typescript next.js

我正在制作一个类似论坛的网站,其中包含一个组件来显示不同的论坛,并使用分页按钮来使用 Next.js 显示下一页。

我当前的页面实现是,我将使用 getServerSideProps 查询数据以进行初始页面加载。之后,当用户单击下一页按钮时显示论坛的下一页。

我想查询在 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中可以看到,“论坛”一词旁边的数字和查询时使用的数字始终为1。

我是 Nextjs 的新手,不理解这种行为。感谢您的帮助。

enter image description here

最佳答案

说明

调用setState不立即更新相关state 。需要重新渲染。这怎么样setState设计作品。作为doc说:

The set function only updates the state variable for the next render. If you read the state variable after calling the set function, you will still get the old value that was on the screen before your call.

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)
}

关于javascript - `setState` 未与UI同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75507057/

相关文章:

javascript - 如何使用ajax和php检查登录 session 状态?

javascript - React 无法读取未定义的属性 'lastName'

javascript - "Non-constructor value passed to NewPromiseCapability"是什么意思?

javascript - React 路由器重定向导致 useEffect 触发两次

reactjs - UI 库的 React Native 性能。我应该使用哪个 : Native base vs React-Native paper?

java - ReactJS 和 Java : Response to preflight request doesn't pass access control check

typescript - 在 typescript npm 模块中重新导出第三方定义

c# - 是否可以通过 HTML 从 jQuery 中的 C#/Razor 获取数据?

javascript - AmplifyJS 状态错误 TypeError : Cannot read property 'state' of undefined

Angular Material : extend a MatPaginator without creating a dupe paginator