Nextjs15 实战 - React Notes CURD 实现
本专栏内容均可在Github:notes_04 找到
完整项目使用技术栈: Nextjs15 + MySQL + Redis + Auth + Prisma + i18n + strapi + Docker + vercel
一、本节目标
本篇我们来实现右侧笔记CURD部分。
一、效果
当点击 New
按钮的时候进入编辑界面:
当点击具体笔记的 Edit
按钮的时候进入该笔记的编辑页面:
二、笔记预览
回忆下之前的路由设计,当点击 New 的时候,导航至 /note/edit
路由,当点击 Edit 的时候,导航至 /note/edit/xxxx
路由。
新增 app/note/edit/page.tsx
import NoteEditor from "../../components/NoteEditor";export default async function EditPage() {return <NoteEditor noteId={""} initialTitle="Untitled" initialBody="" />;
}
新增 app/note/edit/loading.tsx
export default function EditSkeleton() {return (<divclassName="note-editor skeleton-container"role="progressbar"aria-busy="true"><div className="note-editor-form"><div className="skeleton v-stack" style={{ height: "3rem" }} /><div className="skeleton v-stack" style={{ height: "100%" }} /></div><div className="note-editor-preview"><div className="note-editor-menu"><divclassName="skeleton skeleton--button"style={{ width: "8em", height: "2.5em" }}/><divclassName="skeleton skeleton--button"style={{ width: "8em", height: "2.5em", marginInline: "12px 0" }}/></div><divclassName="note-title skeleton"style={{ height: "3rem", width: "65%", marginInline: "12px 1em" }}/><div className="note-preview"><div className="skeleton v-stack" style={{ height: "1.5em" }} /><div className="skeleton v-stack" style={{ height: "1.5em" }} /><div className="skeleton v-stack" style={{ height: "1.5em" }} /><div className="skeleton v-stack" style={{ height: "1.5em" }} /><div className="skeleton v-stack" style={{ height: "1.5em" }} /></div></div></div>);
}
你可能会问,同级的 page.js 又没有数据请求,添加 loading.js 有什么用?
同级的 page.js
确实没有请求,但 loading.js
会将 page.js
和其 children
都包裹在 <Suspense>
中,所以 app/note/edit/[id]/page.tsx 中的请求也会触发该 loading.js
。
app/note/edit/[id]/page.tsx 代码如下:
import NoteEditor from "../../../components/NoteEditor";
import { getNote } from "@/lib/redis";export default async function EditPage({ params }: { params: { id: string } }) {const noteId = params.id;const note = await getNote(noteId);// 让效果更明显const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));await sleep(500);if (note === null) {return (<div className="note--empty-state"><span className="note-text--empty-state">Click a note on the left to view something! 🥺</span></div>);}return (<NoteEditornoteId={noteId}initialTitle={note.title}initialBody={note.content}/>);
}
我们抽离了一个 <NoteEditor>
组件用于实现编辑功能,app/components/NoteEditor.tsx 代码如下:
"use client";import { useState } from "react";
import NotePreview from "@/components/NotePreview";
import { useFormStatus } from "react-dom";export default function NoteEditor({ noteId, initialTitle, initialBody }) {const { pending } = useFormStatus();const [title, setTitle] = useState(initialTitle);const [body, setBody] = useState(initialBody);const isDraft = !noteId;return (<div className="note-editor"><form className="note-editor-form" autoComplete="off"><label className="offscreen" htmlFor="note-title-input">Enter a title for your note</label><inputid="note-title-input"type="text"value={title}onChange={(e) => {setTitle(e.target.value);}}/><label className="offscreen" htmlFor="note-body-input">Enter the body for your note</label><textareavalue={body}id="note-body-input"onChange={(e) => setBody(e.target.value)}/></form><div className="note-editor-preview"><form className="note-editor-menu" role="menubar"><buttonclassName="note-editor-done"disabled={pending}type="submit"role="menuitem"><imgsrc="/checkmark.svg"width="14px"height="10px"alt=""role="presentation"/>Done</button>{!isDraft && (<buttonclassName="note-editor-delete"disabled={pending}role="menuitem"><imgsrc="/cross.svg"width="10px"height="10px"alt=""role="presentation"/>Delete</button>)}</form><div className="label label--preview" role="status">Preview</div><h1 className="note-title">{title}</h1><NotePreview>{body}</NotePreview></div></div>);
}
因为需要控制输入框的状态,所以 <NoteEditor>
使用了客户端组件,我们在 <NotePreview>
中引用了 <NotePreview>
组件,用于实现编辑时的实时预览功能。
三、笔记新增、更新、删除
当点击 Done
的时候,导航至对应的笔记预览页面 /note/xxxx
。当点击 Delete
的时候,导航至首页。
正常开发笔记的增加、更新和删除功能,为了实现前后端交互,可能要写多个接口来实现,比如当点击删除的时候,调用删除接口,接口返回成功,前端跳转至首页。但既然我们都用了 Next.js 15
了,没必要这么麻烦,Server Actions
直接搞定,省的一个个写接口了。
修改 app/components/NoteEditor.tsx
"use client";import { useState } from "react";
import NotePreview from "./NotePreview";
import { useFormStatus } from "react-dom";
import { deleteNote, saveNote } from "../actions";
export default function NoteEditor({noteId,initialTitle,initialBody,
}: {noteId: string;initialTitle: string;initialBody: string;
}) {const { pending } = useFormStatus();const [title, setTitle] = useState(initialTitle);const [body, setBody] = useState(initialBody);const isDraft = !noteId;return (<div className="note-editor"><form className="note-editor-form" autoComplete="off"><label className="offscreen" htmlFor="note-title-input">Enter a title for your note</label><inputid="note-title-input"type="text"value={title}onChange={(e) => {setTitle(e.target.value);}}/><label className="offscreen" htmlFor="note-body-input">Enter the body for your note</label><textareavalue={body}id="note-body-input"onChange={(e) => setBody(e.target.value)}/></form><div className="note-editor-preview"><form className="note-editor-menu" role="menubar"><buttonclassName="note-editor-done"disabled={pending}type="submit"formAction={() => saveNote(noteId, title, body)}role="menuitem">Done</button>{!isDraft && (<buttonclassName="note-editor-delete"disabled={pending}formAction={() => deleteNote(noteId)}role="menuitem">Delete</button>)}</form><div className="label label--preview" role="status">Preview</div><h1 className="note-title">{title}</h1><NotePreview>{body}</NotePreview></div></div>);
}
新增:app/actions.tsx
"use server";import { redirect } from "next/navigation";
import { addNote, updateNote, delNote } from "@/lib/redis";export async function saveNote(noteId: string, title: string, body: string) {const data = JSON.stringify({title,content: body,updateTime: new Date(),});if (noteId) {updateNote(noteId, data);redirect(`/note/${noteId}`);} else {const res = await addNote(data);redirect(`/note/${res}`);}
}export async function deleteNote(noteId: string) {delNote(noteId);redirect("/");
}
至此 新增和删除可以正常运行了:
相关文章:
Nextjs15 实战 - React Notes CURD 实现
本专栏内容均可在Github:notes_04 找到 完整项目使用技术栈: Nextjs15 MySQL Redis Auth Prisma i18n strapi Docker vercel 一、本节目标 本篇我们来实现右侧笔记CURD部分。 一、效果 当点击 New 按钮的时候进入编辑界面: 当点击…...
【KWDB 创作者计划】架构设计与AIoT场景实践
产品定位与核心价值主张 架构设计与技术实现 分布式架构设计 多模存储引擎实现 云边端协同机制 核心技术创新解析 就地计算技术 自适应时序引擎 混合事务处理 性能优化技术体系 高效存储机制 查询加速策略 资源管理与隔离 行业解决方案与典型应用 工业物联网平台…...
DeepSeek底层揭秘——《推理时Scaling方法》技术对比浅析
4月初,DeepSeek 提交到 arXiv 上的最新论文正在 AI 社区逐渐升温。 笔者尝试对比了“关于推理时Scaling”与现有技术,粗浅分析如下: 与LoRA的对比 区别: 应用场景:LoRA是一种参数高效微调方法,主要用于在…...
Spring MVC与Spring Boot文件上传配置差异对比及文件上传关键类详细说明与对比
一、Spring MVC与Spring Boot文件上传配置差异对比 1. 配置方式差异 框架配置方式依赖管理自动配置Spring MVC需手动配置MultipartResolver(如StandardServletMultipartResolver)需自行引入commons-fileupload等依赖无,默认不启用文件上传支…...
Linux网络配置与测试
目录 一.与网络配置相关的命令 1.1ifconfig命令 1.1.1作用 1.1.2网络接口的信息 接口信息的组成 1.1.3显示所有网卡包括没有启动的网卡 1.1.4查看指定网络接口 1.1.5开启或关闭网卡 1.1.6设置临时虚拟网卡 1.1.7网络通讯情况 编辑 1.1.8临时修改网卡属性 1.2hos…...
游戏赛季和数据处理
问题 游戏从无赛季到赛季机制会涉及哪些问题: 如何改动,增加赛季机制,涉及要修改的代码量最少如何改动,账号、角色部分数据继承问题,涉及要修改的代码量最少账号下角色的永久服共享或是永久服独立,需要做…...
京东店铺托管7*16小时全时护航
内容概要 京东店铺托管服务的*716小时全时护航模式,相当于给商家配了个全年无休的"运营管家"。专业团队每天从早7点到晚11点实时盯着运营数据和商品排名,连半夜流量波动都能通过智能系统秒级预警。这种全天候服务可不是单纯拼人力——系统自动…...
HTTP的Keep-Alive是什么?TCP 的 Keepalive 和 HTTP 的 Keep-Alive 是一个东西吗?
HTTP的Keep-Alive: HTTP Keep-Alive 是一种机制,允许客户端和服务器在单个 TCP 连接 上发送多个 HTTP 请求 和 响应,而不是每次请求和响应后都关闭连接。它的主要目的是提高性能,减少连接的开销,优化通信效率。 工作…...
使用scoop一键下载jdk和实现版本切换
安装 在 PowerShell 中输入下面内容,保证允许本地脚本的执行: set-executionpolicy remotesigned -scope currentuser然后执行下面的命令安装 Scoop: iwr -useb get.scoop.sh | iex国内用户可以使用镜像源安装:powershell iwr -us…...
PPIO × UI-TARS:用自然语言操控电脑,AI Agent 的极致体验
Manus的爆火预示着AI 正在从单纯的文本生成和图像识别迈向更复杂的交互场景。字节跳动近期推出的开源项目 UI-TARS Desktop 为我们展示了一种全新的可能性:能够通过自然语言理解和处理来控制计算机界面。这款工具代表了人工智能与人机交互领域的重大突破,…...
PG:incorrect prev-link
目录 WAL日志中"incorrect prev-link"错误解决方案错误原因分析解决步骤典型修复案例 WAL日志中"incorrect prev-link"错误解决方案 错误原因分析 WAL日志的prev-link字段用于确保日志记录的连续性。当出现incorrect prev-link 2/754ECB0 at 2/8000028错…...
SQL Server 数据库邮件配置失败:SMTP 连接与权限问题
问题现象: 配置数据库邮件时,发送测试邮件失败,提示 “邮件无法发送到 SMTP 服务器,操作超时”(错误 14661)或 “服务器拒绝发件人地址”(错误 15009)。 快速诊断 检查数据库邮件配置…...
深入浅出动态规划:从基础到蓝桥杯实战(Java版)
引言:为什么你需要掌握动态规划? 动态规划(DP)是算法竞赛和面试中的常客,不仅能大幅提升解题效率(时间复杂度通常为O(n)或O(n))[4],更是解决复杂优化问题的利器。统计显示ÿ…...
获取cookie的chrome插件:Get cookies.txt LOCALLY
接上一篇,在下载视频的时候需要网站的cookie,下面介绍一款可以获取网站cookie的chrome插件 https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc?utm_sourceitem-share-cb 备注需要科学上网 【使用方…...
opencv无法设置禁用RGB转换问题
树莓派连接摄像头,摄像头输出格式为YUYV(YUV422)。 通过执行 v4l2-ctl --list-formats --device/dev/video0 可以看的具体的摄像头的数据格式。 使用opencv获取视频流,通过cap.set(cv2.CAP_PROP_CONVERT_RGB, 0)设置禁用自动转换RGB格式,但是打印输出…...
Ansible:roles角色
文章目录 Roles角色Ansible Roles目录编排Roles各目录作用创建 roleplaybook调用角色调用角色方法1:调用角色方法2:调用角色方法3: roles 中 tags 使用实战案例 Roles角色 角色是ansible自1.2版本引入的新特性,用于层次性、结构化…...
SAP系统采购信息记录失效
问题:采购信息记录失效 现象:最初主数据导入完成之后,单元测试的时采购信息记录是有效的,中间经过配置的变化,集成测试初期发现采购信息记录全部失效。 原因: 单元测试时发现采购订单里面的条件类型…...
JavaWeb 课堂笔记 —— 04 Ajax
本系列为笔者学习JavaWeb的课堂笔记,视频资源为B站黑马程序员出品的《黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖SpringMyBatisSpringMVCSpringBoot等)》,章节分布参考视频教程,为同样学习…...
Pandas 库
Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据 Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够…...
4.8学习总结
完成摆动序列的算法题(比较难,想不出方法) 学习了HashMap,TreeMap 的源码(看完一遍对其理解没有太清楚,还需再多刷几遍理解源码及其底层逻辑的概念) 学习了可变参数和Collections工具类...
C语言之九九乘法表
一、代码展示 二、运行结果 三、代码分析 首先->是外层循环是小于等于9的 然后->是内层循环是小于等于外层循环的 最后->就是\n让九九乘法表的格式更加美观(当然 电脑不同 有可能%2d 也有可能%3d) 四、与以下素数题目逻辑相似 五、运行结果...
【Linux操作系统】:信号
Linux操作系统下的信号 一、引言 首先我们可以简单理解一下信号的概念,信号,顾名思义,就是我们操作系统发送给进程的消息。举个简单的例子,我们在写C/C程序的时候,当执行a / 0类似的操作的时候,程序直接就挂…...
skynet.call使用详解
目录 skynet.call 详细解析1. 函数签名与参数2. 内部实现机制3. 会话ID与协程调度4. 超时与错误处理5. 返回值处理6. 协议类型的影响7. skynet.call vs skynet.send8. 示例代码分析9. 最佳实践10. 总结 skynet.call 详细解析 1. 函数签名与参数 函数签名: skynet…...
uniapp 打包 H5 向 打包的APP 使用 @dcloudio/uni-webview-js 传值
1.安装 dcloudio/uni-webview-js npm install dcloudio/uni-webview-js -save 这个模块的 uni. 会与H5的uniapp的 uni. 冲突,所以需要改下名称,一共需要改3处 2.引入并使用 import uniWeb from dcloudio/uni-webview-js;uniWeb.postMessage({data: {action: message,content…...
c语言 文件操作
c语言 文件操作 one 打开/usr/dev.txt文件,在第1行 覆盖写入 "MAC1q23456789" #include <fcntl.h> #include <unistd.h> #include <string.h> int main() { const char *line_1 "MAC1q23456789\n"; // 要写入的内容…...
企业电子招投标采购系统——功能模块功能描述+数字化采购管理 采购招投标
功能描述 1、门户管理:所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含:招标公告、非招标公告、系统通知、政策法规。 2、立项管理:企业用户可对需要采购的项目进行立项申请,并提交审批,查看…...
Python 序列构成的数组(序列的增量赋值)
序列的增量赋值 增量赋值运算符 和 * 的表现取决于它们的第一个操作对象。简单起 见,我们把讨论集中在增量加法()上,但是这些概念对 * 和其他 增量运算符来说都是一样的。 背后的特殊方法是 iadd (用于“就地加法”&…...
力扣hot100【链表】
160.相交链表 题目 我的思路:两个链表一长一短,先把长的提前遍历使两个链表的长度相等,然后同时遍历,如果遍历的节点相等时说明相交,否则不相交。 /*** Definition for singly-linked list.* struct ListNode {* …...
PyTorch 生态迎来新成员:SGLang 高效推理引擎解析
SGLang 现已正式融入 PyTorch 生态系统!此次集成确保了 SGLang 符合 PyTorch 的技术标准与最佳实践,为开发者提供了一个可靠且社区支持的框架,助力大规模语言模型(LLM)实现高效且灵活的推理。 如需深入了解 PyTorch…...
C++ Primer Plus 编程练习题 第六章 分支语句和逻辑运算符
1.大小写转换 使用cctype库里的函数进行大小写转换,但要注意使用toupper或tolower时要进行强制类型转换,否则会输出ASCII值 #include <iostream> #include<cctype> using namespace std;int main() {cout << "请输入字符串(大…...
一文详解OpenGL环境搭建:Windows使用CLion配置OpenGL开发环境
在计算机图形学的广阔领域中,OpenGL作为行业标准的图形库,为开发者提供了强大的工具集来创建从简单的2D图形到复杂的3D世界。然而,对于初学者和经验丰富的开发者而言,选择一个合适的开发环境是迈向成功的第一步。尤其是在Windows平台上,配置一个既支持现代C++编程实践又能…...
一次奇怪的enq: TX - row lock contention锁问题处理
某天上午客户告知数据库库有锁导致数据库卡死,需排查出问题的原因,从根本上解决问题。 按正常步骤,查询V$SESSION中BLOCKING_SESSION列不为空的,发现没有进程互相阻塞的情况;而查询ACTIVE会话,则有大量进程…...
STL常用容器整理
STL常用容器操作整理 STL常用容器操作整理(string/vector/set/map)一、string(字符串)构造函数元素访问修改操作容量操作子串与查找 二、vector(动态数组)构造函数元素访问修改操作容量操作 三、set&#x…...
深入 PostgreSQL 内部:5 个关键阶段拆解查询处理全流程
引言 当您向 PostgreSQL 发送查询时,后端会经历多个处理阶段。每个阶段承担着不同的职责,以确保您能在最短时间内获得准确响应。虽然这些阶段可能庞大而复杂,但理解它们在查询处理中的角色对 PostgreSQL 开发者至关重要。本文将概述每个查询…...
解析 LILIkoi 光纤力传感器:FBG 原理铸就耐高温抗干扰优势
LILIkoi光纤力传感器通过光纤光栅(FBG)技术实现高精度力测量。其核心原理基于光纤内光栅栅距的微小变化,用以感知外界施加的力。该传感器在高温、强辐射等恶劣环境中表现出色,能够有效抵抗电磁干扰和温度漂移。凭借卓越的性能&…...
SU-YOLO:基于脉冲神经网络的高效水下目标检测模型解析
论文地址:https://arxiv.org/pdf/2503.24389 目录 一、论文概述 二、创新点解析 1. 基于脉冲的水下图像去噪(SpikeDenoiser) 原理与结构 2. 分离批归一化(SeBN) 原理与结构 3. 优化的残差块(SU-Block) 原理与结构 三、代码复现指南 环境配置 模型训练 四、…...
有关eeprom以及pwm
a0 a1就是对应的 芯片的 写和读 0写 1读 使用操作 主函数读一次 然后信息里一直写入。 用level设置挡位 如 10个格子 设置2 3 这样占空比就有了...
JMeter教程|0到1学会接口性能压测第14课-JMeter接口性能测试全流程讲解
Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。相比Loadrunner而言,JMeter小巧轻便且免费,逐渐成为了主流的性能测试工具,是每个测试人员都必须要掌握的工具之一。 本文以百度搜索接口为例,全流程讲解JMeter接口性能测试。从JMeter下载安装到编写一个…...
系统思考:问题诊断
“做事不怕困难,怕的是不明白困难出在哪里。” —— 亨利福特 最近发现,有些领导者或者团队,常常急于给出解决方案,却忽视了最关键的一步——诊断问题的根源。团队甚至在集体心智模式的影响下,连问题本身都搞错了方向…...
有效压缩 Hyper-v linux Centos 的虚拟磁盘 VHDX
参考: http://www.360doc.com/content/22/0505/16/67252277_1029878535.shtml VHDX 有个不好的问题就是,如果在里面存放过文件再删除,那么已经使用过的空间不会压缩,导致空间一直被占用。那么就需要想办法压缩空间。 还有一点&a…...
使用 redis 实现消息队列
方案1: 使用list做消息队列问题1: 如何保证消息不丢失问题 2: 重复消费/幂等 方案 2: zset实现消息队列方案 3: 发布/订阅(pub/sub)问题1: 如何保证消息不丢失问题 2: 重复消费/幂等 方案 4: Stream 实现消息队列问题1: 如何保证消息不丢失问题 2: 重复消费/幂等 方案1: 使用li…...
2025 XYCTF Pwn-wp(含附件)
前言 总体来说Pwn方向题目难度属于中等,属于那种一眼看不出要咋做,但多试试又能做出来的那种,比赛的时候甚至有几只队伍AK了Pwn方向。感觉题目还是很不错的尽管比赛中有一些小意外像是有些题目附件给错了,但是XYCTF的师傅们都是无偿出题纯热爱向大伙分享自己的题目…...
verilog有符号数的乘法
1、单周期乘法器 对于低速要求的乘法器,可以简单的使用 * 实现。 module Mult(input wire [7:0] multiplicand ,input wire [7:0] multipliter ,output wire [7:0] product);assign product multiplicand * multipliter …...
【python3】关于等额本金和等额本息计算
【python3】关于等额本金和等额本息计算 1.背景2.计算3.总结4.推导 1.背景 在贷款买房的宝子们一定有了解等额本金和等额本息,年轻的时候只听销售在那里计算, 您可能听得云里雾里。 等额本金:每个月还的本金固定,利息逐渐减少。…...
git怎么删除远程分支
删除远程分支 引言删除远程分支查看远程分支查看远程分支详情删除远程分支 引言 本文旨在记录一下,git如何通过命令行删除远程分支。 删除远程分支 查看远程分支 使用指令: git branch -r查看远程分支详情 使用指令: git remote show …...
【C++】函数直接返回bool值和返回bool变量差异
函数直接返回bool值和返回bool变量差异 背景 在工作中遇到一个比较诡异的问题,场景是给业务方提供的SDK有一个获取状态的函数GetStatus,函数的返回值类型是bool,在测试过程中发现,SDK返回的是false,但是业务方拿到的…...
蓝桥杯-蓝桥幼儿园(并查集)
并查集的核心思想 并查集主要由两个操作构成: Find:查找某个元素所在集合的根节点。并查集的特点是,每个元素都指向它自己的父节点,根节点的父节点指向它自己。查找过程中可以通过路径压缩来加速后续的查找操作,即将路…...
Ensemble of differential evolution variants(EDEV)
差分进化变体的集成1 在这项研究中,一个基于多种群的框架(MPF)被提议用于多个差分进化变体的集合。与PAP2不同,PAP通过时间预算分配策略和个体移民算子实现算法组合,MPF将整个种群划分为子种群,包括几个指…...
《AI开发工具和技能实战》第1集 Windows CMD 命令行操作指南:从基础到进阶
第1集 Windows CMD 命令行操作指南:从基础到进阶 在日常使用 Windows 系统时,命令提示符(Command Prompt,简称 CMD)是一个强大且灵活的工具。无论是文件管理、系统配置,还是网络诊断,CMD 都能提…...
实现一个拖拽排序组件:Vue 3 + TypeScript + Tailwind CSS
文章目录 一、项目背景与需求分析需求: 二、搭建基础项目1. 初始化 Vue 3 项目2. 安装 Tailwind CSS 三、设计拖拽排序组件1. 创建拖拽排序组件2. 说明: 四、完善样式与功能1. 样式调整2. 拖拽顺序更新 五、进一步优化与拓展1. 添加排序指示器2. 支持动态…...