Next.js 13 导航路由刷新保留状态

huangapple go评论78阅读模式
英文:

Next.js 13 navigation router refresh preserves state

问题

I have this below component and it's included within a Server Component that fetches data and renders it. As it can be seen, I call router.refresh() on click so it will rerun that page and refetch the data. And it does, but the problem is Next.js keeps preserving this state, so when I call setRefreshing(true) even though the server component reruns, the refreshing state is still true.

'use client'
import { useRouter } from 'next/navigation'
import Button from './Button'

export default function Refresh() {
  const [refreshing, setRefreshing] = useState(false)
  const router = useRouter()

  const handleClick = () => {
    setRefreshing(true)
    router.refresh()
  }

  return (
    <Button onClick={handleClick}>
      <i className={`bi bi-arrow-repeat text-lg ${refreshing ? 'animate-spin' : ''}`}></i> 
      Refresh
    </Button>
  )
}

useEffect is also not running after refresh, so I can't set it to false in there either. I've found a workaround to pass an array prop to the component, which will then rerun useEffect(() => {...}, [serverHackyProp]).

<Refresh inProgress={[false]} />


const [[refreshing], setRefreshing] = useState(inProgress)
const router = useRouter()

useEffect(() => {
  setRefreshing(inProgress)
}, [inProgress])

const handleClick = () => {
  setRefreshing([true])
  router.refresh()
}

It works because the passed array will always have a new reference, so useEffect will run every time the server reruns. But I don't like to do that unless it's my last choice, but I couldn't find a way to setRefreshing(false) after the router refreshed (server refetched).

英文:

I have this below component and it's included withing a Server Component that fetches data and render it. As it can be seen I call router.refresh() on click so it will rerun that page and refetch the data. And it does but the problem is Nextjs keep preserving this state, so when i call setRefreshing(true) even though server component rerun refreshing state is still true.

&#39;use client&#39;
import { useRouter } from &#39;next/navigation&#39;
import Button from &#39;./Button&#39;

export default function Refresh() {
  const [refreshing, setRefreshing] = useState(false)
  const router = useRouter()

  const handleClick = () =&gt; {
    setRefreshing(true)
    router.refresh()
  }

  return (
    &lt;Button onClick={handleClick}&gt;
      &lt;i className={`bi bi-arrow-repeat text-lg ${refreshing ? &#39;animate-spin&#39; : &#39;&#39;}`}&gt;&lt;/i&gt; 
      Refresh
    &lt;/Button&gt;
  )
}

useEffect is also not running after refresh so i can't set it to false in there either. I've found a workaround to pass an array prop to the component which then it will rerun useEffect(() =&gt; {...}, [serverHackyProp]).

&lt;Refresh inProgress={[false]} /&gt;


const [[refreshing], setRefreshing] = useState(inProgress)
const router = useRouter()

useEffect(() =&gt; {
  setRefreshing(inProgress)
}, [inProgress])

const handleClick = () =&gt; {
  setRefreshing([true])
  router.refresh()
}

It works because passed array will always have a new reference so useEffect will run every time server rerun. But I don't like to do that unless it's my last choice but i couldn't find a way to setRefreshing(false) after router refreshed (server refetched).

答案1

得分: 0

这个问题的一个简单解决方案是让你的数据获取函数在响应中返回一些随机值,并将该随机值用作你的 Refresh 组件的键。这样,每当服务器端数据刷新时,你的 Refresh 组件就会被销毁并重新创建。

// 仅用于测试目的,将 revalidate 设置为 0
export const revalidate = 0;

async function getData() {
  return { data: ..., sid: Math.random() };
}

export default async function Page() {
  const data = await getData();

  return (
    <>
      <Refresh key={data.sid} />
      ...
    </>
  );
}
英文:

One trivial solution to this problem is to have your data fetching function return some random value as part of the response, and use that random value as a key of your Refresh component. This way your Refresh component will be teared down and re-created every time server-side data is refreshed.

// setting revalidate to 0 just for the testing purpose
export const revalidate = 0;

async function getData() {
  return { data: ..., sid: Math.random() };
}

export default async function Page() {
  const data = await getData();

  return (
    &lt;&gt;
      &lt;Refresh key={data.sid} /&gt;
      ...
    &lt;/&gt;
  );
}

huangapple
  • 本文由 发表于 2023年6月27日 17:49:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76563631.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定