学习Next.js如何在后台工作

985 阅读4分钟

在我正在开发的一个Next.js应用程序中,我有一个 "管理 "页面,它让我可以管理注册用户。

我最喜欢Next.js的一个特点是,单个路由可以选择加入服务器端渲染。虽然我倾向于大力提倡静态生成,但这是一个完美的服务器端渲染的用例;我可以在第一次渲染时获取并注入数据库数据,简化我的前端代码。

至少,我是这么想的......然后我遇到了一个障碍。😬

我的问题是这样的:在我的仪表板上,我可以编辑用户,以授予购买权或更新他们的名字。

注意到更新后的名字没有显示在表格中吗?这是因为页面不知道基础数据已经改变。在页面被服务器渲染后,这些道具是不可改变的。

我们如何告诉Next.js按需重新获取数据,而不对整个页面进行硬刷新?

在这个简短的教程中,我将分享我学到的解决这个问题的灵巧技巧。我们还将学习Next.js如何在后台工作,并涵盖一些相关的问题和解决方案。

该解决方案

这就是解决方案,对于忙碌的海狸来说,它是一个复制粘贴的胜利。

import { useRouter } from 'next/router';
function SomePage(props) {
  const router = useRouter();
  // Call this function whenever you want to
  // refresh props!
  const refreshData = () => {
    router.replace(router.asPath);
  }
}
export async function getServerSideProps(context) {
  // Database logic here
}

每当你想从后端提取新的数据时,就会调用refreshData 函数。这将根据你的使用情况而变化。作为一个例子,这里是你在修改了一个用户之后如何刷新数据的。

async function handleSubmit() {
  const userData = /* create an object from the form */
  const res = await fetch('/api/user', {
    method: 'PUT',
    body: JSON.stringify(userData),
  });
  // Check that our status code is in the 200s,
  // meaning the request was successful.
  if (res.status < 300) {
    refreshData();
  }
}

但为什么这样做呢?为什么我们要让路由器参与这个过程,它到底在做什么?

事实上,这个解决方案需要一些背景设置。让我们来谈一谈服务器渲染的路由如何有一个秘密的替代者🦸🏾♀️

为什么它能发挥作用

当我们想到getServerSideProps ,我们通常会想象一个看起来像这样的流程。

  • 用户从谷歌(或其他地方)跟踪一个链接到我们的网站。

  • Next.js调用我们的getServerSideProps 方法,并使用它来生成一个HTML文件。

  • 用户收到了该HTML文件,React在客户端上进行补水。

这就是Next.js服务器路由的肉和土豆。这是克拉克-肯特在日常工作中所做的文书工作。

但Next.js还有另一种方式使用你的getServerSideProps--从客户端。

考虑一个不同的场景。

  • 用户已经在你的网站上,他们点击Next.js的Link ,导航到服务器渲染的页面。

  • Next.js在服务器上调用你的getServerSideProps 方法,但它没有生成一个HTML文件,而是将数据以JSON格式发送到客户端。

  • React在浏览器中渲染新页面时,使用该数据作为初始道具。

Next.js之所以这么酷,是因为服务器渲染代码可以作为一种API来使用。我们可以用Next实现闪电般的客户端路由,因为你的服务器渲染的路由可以跳进电话亭,穿上服装,成为一个API端点。白天是服务器渲染者,晚上是JSON发送者。

我们的解决方案是有效的,因为我们正在执行客户端过渡到同一路线。 router.asPath 是对当前路径的引用。如果我们在/admin-panel ,我们就告诉Next做一个客户端重定向到/admin-panel ,这将导致它重新获取JSON数据,并将其作为props传递给当前页面。🧨

如果你想知道router.replace :它就像router.push ,但它不会在历史栈中添加一个项目。我们不希望它被 "算作 "重定向,这样浏览器的 "返回 "按钮仍能按我们的意图工作。

简而言之:Next.js没有 "refetchProps "方法,但我们可以利用客户端的导航行为来达到同样的目的。💯

加载状态

当你调用这个方法时,在你的用户界面中不会有任何迹象表明正在发生重新获取。这在某些情况下是可以的,但我们不能假设一个快速的网络;即使你的getServerSideProps 呼叫是Blazing Fast™,一个在树林里的用户在1 bar的3G下仍然会被卡住等待一分钟的时间。

我们需要一个加载状态!下面是我们如何创建一个。

function SomePage({ theData }) {
  const [isRefreshing, setIsRefreshing] = React.useState(false);
  const refreshData = () => {
    router.replace(router.asPath);
    setIsRefreshing(true);
  };
  React.useEffect(() => {
    setIsRefreshing(false);
  }, [theData]);
}

我们有一个新的React状态变量,isRefreshing 。当我们发出请求时,我们把它设置为true ,当组件用新数据重新渲染时,它又被设置为false

我们不在每次渲染时启动它,而是把它放在一个效果钩中,并跟踪我们的theData 道具(theData 是一个占位符,代表你的服务器数据的样子)。当服务器返回新的数据时,钩子就会启动,而我们的加载状态就会终止。它可以保护我们免受 "偶然 "渲染的影响,如果在我们等待数据的时候,其他状态恰好发生了变化。

下面是它的样子。请注意右上角的加载指示灯。

突变数据

在我的管理-仪表盘案例中,我不需要对服务器渲染的数据做任何 "特别 "的修改。一个直接的刷新就是我所寻找的全部。

但如果我想以某种方式改变数据呢?也许我想做一个 "乐观的更新",服务器确认之前显示新状态的数据?

在这种情况下,我们必须将道具转移到状态中,这样它就可以像其他React状态一样被改变。

function SomePage({ initialData }) {
  const [theData, setTheData] = React.useState(initialData);
  // Mutate whenever you want with `setTheData`!
}

现在,你可能在想:把道具复制到状态中不是一种反模式吗?React文档不是告诉我们不要做这件事吗?

并非如此。我们要避免的是重复真相的来源。如果多个组件定义了相同的状态,这通常是一个问题的标志。

在这种情况下,我们只有一个真理之源,而且是在我们的React树的最顶端。对我来说,这闻起来像Spring Breeze织物柔软剂。是代码的香味,不是代码的味道。

建议在prop前加上initial (例如:initialUsers ,而不是users ),这样就可以清楚地知道prop作为一个初始值,而不是一个持续的真理源。

替代方案

在我发现路由器刷新技巧之前,我的游戏计划是这样的。

  1. 把数据库的调用从getServerSideProps ,拉到一个函数中,getUsers 。在getServerSideProps 中调用该函数。

  2. 创建一个API路由使用getUsers 数据,并以JSON格式返回。

  3. 在页面组件中,使用一个像SWR这样的库来跟踪数据。它将从服务器端的道具中初始化,但连接到新的API路由以进行后续的数据获取。

  4. 根据需要使用SWR来改变数据。

在我的特定情况下,这条路径对我来说感觉过于复杂了;我不希望有两个独立的机制来获取用户!而且,虽然,但这是一个很好的库。虽然SWR 是一个伟大的库,我在我的应用程序中使用它来管理认证,但在这种情况下感觉有点沉重。

但是,在其他情况下,我认为这将是正确的方法。例如,如果你有复杂的数据获取或数据变异的要求,或者你已经有一个可以在客户端直接交互的API。

收尾工作

这是我在Next.js上的第一个教程!🍾