本文根据项目路线实现问题更新功能。
添加编辑按钮
在IssueDetailPage右侧添加编辑按钮,将页面分成两列。对于手机则仅显示单列。
使用Grid组件,响应式显示,初始1列,当屏幕宽度至md时,就显示2列。另外,我们添加的按钮放置一个图标,需要安装radix ui的图标库。
npm i @radix-ui/react-icons
页面显示。
提交Git,命名:Add the edit button.
应用单一责任原则
软件工程有个单一责任原则(Single Responsibility Principle)布局,意思每个模块只专注于一个事情。查看IssueDetailPage,它关注了布局,同时也实现了每个布局块的细节,我们应该将每个布局块的细节移到其他地方。
我们新建问题详细查看组件IssueDetails(左侧)和按钮组件EditIssueButton(右侧),并将页面的相关实现细节移到这两个组件,而页面本身只关注布局。
现在,我们IssueDetailPage就很清晰了。
提交Git,命名:Refactor:Apply the SRP.
构建编辑问题页面
编辑页面与新建页面是一样的,我们将NewIssuePage的内容全部剪切出来形成IssueForm。该组件放在哪里呢?根目录的components不合适,因为IssueForm只用于Issue, 我们在issues目录下新建_components目录,再新建IssueForm.tsx,然后NewIssuePage调用它。注意创建的_components目录是以下划线开头,这样就在路由系统之外,即便添加page.tsx也是无效路径。
在 /issues/[id]下新建edit目录,再新建page.tsx。编辑页面与新建不同的是,它通过id 参数获取 数据库信息,并填充表单。
提交Git,命名:Build the edit issue page.
构建 API
api/issues/ 下新建目录 [id],创建文件route.tsx,创建PATCH函数。验证请求体数据时用zod,将之前的createIssueSchema改成issueSchema. 其它都是常规做法。
// api/issues/[id]/route.tsx
import { issueSchema } from "@/app/validationSchems";
import prisma from "@/prisma/client";
import { NextRequest, NextResponse } from "next/server";
export async function PATCH(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const body = await request.json();
const validation = issueSchema.safeParse(body);
if (!validation.success)
return NextResponse.json(validation.error.format(), { status: 400 });
const issue = await prisma.issue.findUnique({
where: {
id: parseInt(params.id),
},
});
if (!issue)
return NextResponse.json({ error: "Invalid issue" }, { status: 404 });
const updatedIssue = await prisma.issue.update({
where: { id: parseInt(params.id) },
data: {
title: body.title,
description: body.description,
},
});
return NextResponse.json(updatedIssue);
}
提交Git,命名:Build an API for updating issues.
更新问题
建好了更新API,下一步就来修改IssueForm,使之可以实现更新。
在Form的handleSubmit,判断 issue是否存在,存在就是更新,不存在就是新建,并修改提交按钮的显示。
提交Git,命名:Update issues.
了解缓存
新建一条问题提交返回问题列表时,我们发现刚建的问题并未在列表中,这就涉及到Next.js的缓存了!
有三个层次的缓存:
- 数据缓存。当使用fetch()时会有数据缓存,可以自定义缓存的方式。我们这里没用fetch(), 而是用axios,所以没有数据缓存。
2.全路由缓存或者叫”服务器上的缓存”,用于存储静态渲染的输出。前一系列我们说过有动态渲染和静态渲染,静态渲染发生在构建时,而动态渲染则发生在每次请求时。
默认情况,Next.js将带参数的路径如 /issues/1 使用动态渲染,而不带参数的路径 /issues 使用静态渲染。实际看一下:npm run build。能看到 /issues是静态的,而 /issues/[id]则是动态的。
可以手动修改这个特性。搜索”nextjs route sengment config” ,找到网页查看说明。
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
修改 issues 页面,添加常数dynamic:
export const dynamic = ‘force-dynamic’;
重新进行构建:npm run build. 就能看到 /issues 已变成动态渲染了。目前我们进行添加一条问题返回列表时,还是看不到新建的问题的,这是由于还有一层缓存叫客户端缓存在起作用,下面就会讲到。
前面的dynamic也可用revalidate代替,其中参数0表示每隔0秒进行刷新,这跟之前用dynamic的效果是一样的,当然我们也可以提高数字,比如60,表示每60秒就刷新。
export const revalidate = 0
3. 路径缓存或叫“客户端缓存”,它存在于客户端浏览器的内存中。Next.js自动缓存页面的有效负载,用户返回某个页面时,自动从缓存取出而不必从后端获取。客户端缓存只持续一个session时期,所以当用户刷新页面时,就会从后端取数据。
当页面是静态渲染时,缓存时间是5分钟;页面是动态渲染时,缓存时间是30秒。可以使用router.refresh()手动刷新。
提交Git,命名:Fix caching issues.
改善加载体验
在NewIssuePage, title输入框与description的markdown编辑不是同时加载的,原因是markdown需要延迟加载,而title框不是。这种体验比较怪异,我们需要将它们调整成同时延迟加载。
先去掉IssueForm的Markdown延迟加载代码.
在 NewIssuePage添加延迟代码。
注意,我们在NewIssuePage里添加了loading属性,用于加载IssueFormSkeleton。这个组件是复制于 loading.tsx的。
而原来的loading改成导入IssueFormSkeleton。
EditIssuePage同样修改成整体延迟加载。
在网页测试刷新新建问题页面及编辑问题页,加载同步已明显改善。
提交Git,命名:Improving the loading experience.
小结
本文实现了问题编辑更新功能。
下一篇实现问题的删除功能。