用 React 编写一个笔记应用程序

这篇文章会教大家用 React 编写一个笔记应用程序。用户可以创建、编辑、和切换 Markdown 笔记。
在这里插入图片描述
在这里插入图片描述

1. nanoid

nanoid 是一个轻量级和安全的唯一字符串ID生成器,常用于JavaScript环境中生成随机、唯一的字符串ID,如数据库主键、会话ID、文件名等场景。

2. React-split

react-split 是一个 React 组件库,它提供了一个简单易用的界面来分割面板,允许用户通过拖动来调整面板的大小。这个库非常适合需要在界面上展示可调整大小的多个区域的场景,比如编辑器、IDE、仪表板等。

import Split from "react-split";

<Split>
  <div>Panel 1</div>
  <div>Panel 2</div>
</Split>

3. react-mde

react-mde 是一个用于 React 的 Markdown 编辑器组件,它基于 Draft.js 构建,提供了一个功能强大且可扩展的界面,允许用户以 Markdown 语法或可视化方式编辑文本。

npm install react-mde --legacy-peer-deps

4. showdown

Showdown 是一个 JavaScript 库,用于将 Markdown 文本转换为 HTML。它是一个广泛使用的库,因为它兼容性好、速度快,且易于集成和使用。

5. App.jsx

这是一个用 React 编写的笔记应用程序的主组件 App。用户可以创建、编辑、和切换 Markdown 笔记。它使用了以下功能与库:

  • React:实现状态管理和组件化。
  • nanoid:生成唯一的 ID,用于每个笔记。
  • react-split:实现分屏布局(侧边栏和编辑器)。
  • 自定义组件:
    – Sidebar:显示笔记列表和用于切换当前笔记。
    – Editor:用于编辑当前笔记的内容。

主要功能

  1. 创建新笔记
  2. 更新笔记内容
  3. 切换当前笔记
  4. 显示分屏布局
  5. 无笔记时显示提示
import React from "react";
import { nanoid } from "nanoid";
import Split from "react-split";
import Sidebar from "../public/components/Sidebar";
import Editor from "../public/components/Editor";

  • React:提供 useState 和其他功能,用于创建 React 组件。
  • nanoid:生成唯一的 ID,用于标识每条笔记。
  • Split:用于实现分屏布局(侧边栏 + 编辑器)。
  • Sidebar 和 Editor 是自定义的子组件,分别处理笔记列表和笔记编辑。

状态管理

const [notes, setNotes] = React.useState([]);
const [currentNoteId, setCurrentNoteId] = React.useState(
  (notes[0] && notes[0].id) || ""
);

  • notes:存储所有笔记的状态,初始值为空数组。
  • currentNoteId:存储当前笔记的 ID,初始值为第一个笔记的 ID(若无笔记,则为空字符串)。

创建新笔记

function createNewNote() {
  const newNote = {
    id: nanoid(),
    body: "# Type your markdown note's title here"
  };
  setNotes(prevNotes => [newNote, ...prevNotes]);
  setCurrentNoteId(newNote.id);
}

  • 使用 nanoid 生成一个唯一的 ID。
  • 创建一个新笔记对象,默认内容为 “# Type your markdown note’s title here”。
  • 将新笔记添加到 notes 的顶部。
  • 更新 currentNoteId 为新笔记的 ID

更新笔记

function updateNote(text) {
  setNotes(oldNotes => oldNotes.map(oldNote => {
    return oldNote.id === currentNoteId
      ? { ...oldNote, body: text }
      : oldNote;
  }));
}

  • 遍历 notes,检查每条笔记的 ID。
  • 如果某条笔记的 ID 与 currentNoteId 相同,则更新其 body 内容。
  • 使用 ...oldNote 保留笔记其他的属性,更新 body

查找当前笔记

function findCurrentNote() {
  return notes.find(note => {
    return note.id === currentNoteId;
  }) || notes[0];
}

  • 查找与 currentNoteId 匹配的笔记。
  • 如果没有匹配到,返回第一条笔记(默认情况)。
return (
  <main>
    {notes.length > 0 ? (
      <Split
        sizes={[30, 70]}
        direction="horizontal"
        className="split"
      >
        <Sidebar
          notes={notes}
          currentNote={findCurrentNote()}
          setCurrentNoteId={setCurrentNoteId}
          newNote={createNewNote}
        />
        {currentNoteId && notes.length > 0 && (
          <Editor
            currentNote={findCurrentNote()}
            updateNote={updateNote}
          />
        )}
      </Split>
    ) : (
      <div className="no-notes">
        <h1>You have no notes</h1>
        <button
          className="first-note"
          onClick={createNewNote}
        >
          Create one now
        </button>
      </div>
    )}
  </main>
);

有笔记时的渲染:

  • 使用 Split 实现水平分屏,30% 分给侧边栏(Sidebar),70% 分给编辑器(Editor)。
  • Sidebar:显示笔记列表,并支持切换当前笔记或创建新笔记。
  • Editor:编辑当前笔记的内容。

无笔记时的渲染:

  • 显示提示文字 You have no notes
  • 提供一个按钮 Create one now,单击后调用 createNewNote 创建第一条笔记。

6. Sidebar.jsx

这是一个 React 组件 Sidebar,用于显示笔记的侧边栏列表,并允许用户切换当前笔记或创建新笔记。此组件通过 props 接收父组件传递的状态和函数来实现动态渲染和交互。

import React from "react";

export default function Sidebar(props) {

  • React:用于支持 React 的 JSX 语法。
  • export default:将 Sidebar 组件作为默认导出,使其可被其他模块导入。

Sidebar 是一个无状态函数组件,接收 props 参数(即父组件传递的数据)。

生成笔记元素

const noteElements = props.notes.map((note, index) => (
    <div key={note.id}>
        <div
            className={`title ${
                note.id === props.currentNote.id ? "selected-note" : ""  
            }`}
            onClick={() => props.setCurrentNoteId(note.id)}
        >
            <h4 className="text-snippet">Note {index + 1}</h4>
        </div>
    </div>
));

  1. props.notes
  • 父组件传入的笔记数组,每个笔记对象包含 idbody 属性。
  1. props.notes.map()
  • 遍历笔记数组,为每条笔记生成一个 JSX 元素。
  • key={note.id}:为每个顶层元素提供唯一的 key 属性,以优化 React 的渲染性能。
  1. 条件渲染类名:
  • className={title ${note.id === props.currentNote.id ? "selected-note" : ""}}
  • 如果当前笔记的 id 与 props.currentNote.id 匹配,添加 selected-note 类名。
  • 用于高亮显示当前选中的笔记。
  1. 点击事件:
  • onClick={() => props.setCurrentNoteId(note.id)}
  • 点击笔记时调用父组件传入的 setCurrentNoteId 方法,更新当前笔记的 id。
  1. 显示笔记编号:
  • <h4 className="text-snippet">Note {index + 1}</h4>:动态显示笔记的编号(从 1 开始)。

侧边栏结构

return (
    <section className="pane sidebar">
        <div className="sidebar--header">
            <h3>Notes</h3>
            <button 
                className="new-note"
                onClick={props.newNote}
            >+</button>
        </div>
        {noteElements}
    </section>
);

  1. 侧边栏头部:
  • <div className="sidebar--header">:包含标题和创建按钮。
  • 标题:<h3>Notes</h3>
  • 创建按钮:
  • <button className="new-note" onClick={props.newNote}>+</button>
    点击按钮调用父组件传入的 newNote 方法,创建一条新笔记。
  1. 笔记列表:
  • {noteElements}:动态渲染生成的笔记列表。

组件渲染逻辑总结

  1. 笔记渲染:
  • 遍历 props.notes,生成笔记列表。
  • 为选中的笔记添加 selected-note 类名。
  • 点击笔记切换当前选中的笔记。
  1. 笔记创建:
  • 提供一个按钮,点击后调用 props.newNote 创建新笔记。
  1. 动态更新:
  • 通过 props 与父组件共享状态,每次父组件状态更新时,Sidebar 会自动重新渲染。

7. Editor.jsx

此代码定义了一个名为 Editor 的 React 函数组件,用于实现 Markdown 文本编辑器。它通过 ReactMde(React Markdown Editor)库提供一个多功能的 Markdown 编辑和预览界面。

import React from "react"
import ReactMde from "react-mde"
import 'react-mde/lib/styles/css/react-mde-all.css';
import Showdown from "showdown"

  1. React:
  • 导入 React 库,支持 JSX 和组件开发。
  1. ReactMde:
  • 导入 React Markdown Editor,用于渲染 Markdown 编辑器。
  • ReactMde 提供了内置的编辑和预览功能。
  1. 样式文件:
  • 导入 react-mde 的样式文件,应用默认的编辑器样式。
  1. Showdown:
  • 导入 Showdown 库,用于将 Markdown 文本转换为 HTML。
export default function Editor({ currentNote, updateNote }) {
    const [selectedTab, setSelectedTab] = React.useState("write")

  1. 函数组件:
  • 定义一个函数组件 Editor,接收两个 props:
  • currentNote:当前笔记对象(包含 body 属性)。
  • updateNote:更新笔记内容的回调函数。
  1. 状态管理:
  • 使用 React 的 useState Hook 管理当前编辑器的选项卡状态:
  • selectedTab 的初始值为 “write”,表示当前默认显示“编辑”模式。
const convertor = new Showdown.Converter({
    tables: true,
    simplifiedAutoLink: true,
    strikethrough: true,
    tasklists: true,
})

  1. 创建 Showdown.Converter 实例:
  • 配置 Markdown 转换器,使其支持以下扩展功能:
  • tables: true:支持表格语法。
  • simplifiedAutoLink: true:自动将 URL 转换为超链接。
  • strikethrough: true:支持 删除线 语法。
  • tasklists: true:支持任务列表
  1. 作用:将 Markdown 文本转换为 HTML,以便在预览模式中显示。
return (
    <section className="pane editor">
        <ReactMde
            value={currentNote.body}
            onChange={updateNote}
            selectedTab={selectedTab}
            onTabChange={setSelectedTab}
            generateMarkdownPreview={(markdown) => 
                Promise.resolve(convertor.makeHtml(markdown))
            }
            minEditorHeight={80}
            heightUnits="vh"
        />
    </section>
)

ReactMde 编辑器:

  • value:绑定到 currentNote.body,表示当前笔记的内容。
  • onChange:当用户在编辑器中输入内容时,调用 updateNote 更新父组件的状态。
  • selectedTab:当前选中的选项卡(“write” 或 “preview”)。
  • onTabChange:切换选项卡时调用,更新 selectedTab 的状态。
  • generateMarkdownPreview:预览时调用,将 Markdown 文本转换为 HTML。使用 convertor.makeHtml(markdown) 完成转换,返回一个 Promise。
  • minEditorHeightheightUnits:设置编辑器的最小高度为 80,单位为 vh(视口高度)。

父组件如何与 Editor 交互

  • currentNote:父组件将当前笔记对象传递给 Editor。通过 currentNote.body 显示当前笔记的内容。
  • updateNote:父组件提供回调函数,用于更新笔记内容。

8. index.css

* {
  box-sizing: border-box;
}

1.* 通配符选择器:

  • 设置所有元素的 box-sizing 为 border-box。
  • 效果:元素的 width 和 height 属性包括内边距(padding)和边框(border),避免计算宽高时的复杂性。
body {
  margin: 0;
  padding: 0;
  font-family: 'Karla', sans-serif;
}

  1. body 标签:
  • 移除默认的外边距(margin)和内边距(padding)。
  • 设置全局字体为 Karla,使用无衬线字体(sans-serif)作为后备。
button:focus {
  outline: none;
}

  1. 按钮样式:移除按钮在获得焦点(focus)时的默认轮廓(outline)。
.ql-editor p,
.ql-editor.ql-blank::before {
  font-size: 1.3em;
  font-weight: 100;
}

ql-editor

  • 专为 ReactMde(Markdown 编辑器)定义。
  • 调整段落文字大小(1.3em)和字体粗细(100,非常细的字体)。
.pane {
  overflow-y: auto;
}

.pane

  • 应用于侧边栏和编辑器容器。
  • 启用垂直滚动(overflow-y: auto),以显示超出容器高度的内容。
.sidebar {
  width: 20%;
  height: 100vh;
}

.sidebar

  • 设置侧边栏宽度为 20%,高度占满整个视口(100vh)。
.editor {
  width: 80%;
  height: 100vh;
}

.editor

  • 设置编辑器宽度为 80%,高度占满整个视口。
.sidebar--header {
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.sidebar--header
侧边栏标题区域:

  • 使用 flex 布局。
  • 水平排列子元素,且均匀分布(justify-content: space-around)。
  • 垂直居中对齐(align-items: center)。
.new-note, .first-note {
  cursor: pointer;
  background-color: #4A4E74;
  border: none;
  color: white;
  border-radius: 3px;
}

两类按钮(新建笔记 new-note 和初始笔记 first-note):

  • 指针悬停变成手型(cursor: pointer)。
  • 背景色为深紫色(#4A4E74),字体颜色为白色。
  • 圆角(border-radius: 3px)。
.title {
  overflow: hidden;
  width: 100%;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

单个笔记标题的样式:

  • 超出宽度的内容隐藏(overflow: hidden)。
  • 使用 flex 布局,子元素之间均匀分布。
.selected-note {
  background-color: #4A4E74;
}
.selected-note .text-snippet {
  color: white;
  font-weight: 700;
}

当前选中的笔记高亮:

  • 背景色变成深紫色。
  • 子元素 .text-snippet 的字体颜色变成白色,且加粗(font-weight: 700)。
.gutter {
  background-color: #eee;
  background-repeat: no-repeat;
  background-position: 50%;
}

.gutter

  • 分割侧边栏和编辑器的拖拽条。
  • 设置背景色为浅灰色。
.gutter.gutter-horizontal {
  background-image: url('data:image/png;base64,...');
}
.gutter.gutter-horizontal:hover {
  cursor: col-resize;
}

.gutter-horizontal
水平分割条的样式:

  • 使用 base64 图片作为背景。
  • 鼠标悬停时,显示水平调整光标(col-resize)。
.no-notes {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: whitesmoke;
}

当没有任何笔记时的提示区域:

  • 宽度和高度占满视口。
  • 子元素垂直排列(flex-direction: column),水平和垂直居中。
  • 背景色为浅白色(whitesmoke)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/927349.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

#渗透测试#红蓝攻防#HW#漏洞挖掘#漏洞复现01-笑脸漏洞(vsftpd)

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

Day1——GitHub项目共同开发

MarkDowm解释 Markdown是一种轻量级标记语言&#xff0c;它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成结构化的HTML代码。Markdown的目的是让文档的编写和阅读变得更加容易&#xff0c;同时也不失HTML的强大功能。以下是Markdown的一些基本概念和用法&a…

【攻防世界】WEB-inget

首先找到该关卡 启动靶场环境 访问靶场 构造一个id参数&#xff0c;尝试访问&#xff0c;无内容回显 使用sqlmap工具&#xff0c;先获取数据库&#xff0c;输入命令sqlmap -u http://61.147.171.105:58893/?id1 --dbs 发现第一个即为所需数据库&#xff0c;接下来进行获取…

【C++】深度剖析经典编程题目:电影票、A+B与鸡兔同笼的解决方案

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;牛牛买电影票问题题目描述解题思路代码实现优化思路总结 &#x1f4af;AB问题题目描述解题思路代码实现代码优化总结 &#x1f4af;鸡兔同笼问题题目描述解题思路数学解法代…

掌上单片机实验室 — RT - Thread+ROS2 浅尝(26)

前面化解了Micro_ROS通讯问题&#xff0c;并在 RT-Thread Studio 环境下&#xff0c;使用Micro_ROS软件包中的例程&#xff0c;实现了STM32F411CE核心板和ROS2主机的通讯。之后还尝试修改例程 micro_ros_sub_twist.c &#xff0c;实现了接收 turtle_teleop_key 所发出的 turtle…

How to monitor Spring Boot apps with the AppDynamics Java Agent

本文介绍如何使用 AppDynamics Java 代理监视 Azure Spring Apps 中的 Spring Boot 应用程序。 使用 AppDynamics Java 代理可以&#xff1a; 监视应用程序使用环境变量配置 AppDynamics Java 代理 在 AppDynamics 仪表板中检查所有监视数据 How to monitor Spring Boot app…

ComfyUI | ComfyUI桌面版发布,支持winmac多平台体验,汉化共享等技巧!(内附安装包)

ComfyUI 桌面版正式推出&#xff0c;支持 Windows 与 macOS 等多平台&#xff0c;为 AI 绘画爱好者带来全新体验。其安装包便捷易用&#xff0c;开启了轻松上手之旅。汉化共享功能更是一大亮点&#xff0c;打破语言障碍&#xff0c;促进知识交流与传播。在操作上&#xff0c;它…

CAD深度清理工具-AVappsDrawingPurge9.0.0(2024.8.27版本) 支持版本CAD2022-2025-供大家学习研究参考

图形文件DWG体积很大&#xff1a;通常没有明显的数据。同时&#xff0c;还其他症状包括&#xff1a; &#xff08;1&#xff09;无法复制和粘贴图元。 &#xff08;2&#xff09;悬挂较长时间选择文本与 “特性”选项板上打开。 &#xff08;3&#xff09;图形文件需要很长时间…

鸿蒙Next星河版基础用例

目录&#xff1a; 1、鸿蒙箭头函数的写法2、鸿蒙数据类型的定义3、枚举的定义以及使用4、position绝对定位及层级zIndex5、字符串的拼接转换以及数据的处理(1)字符串转数字(2)数字转字符串(3)布尔值转换情况(4)数组的增删改查 6、三元表达式7、鸿蒙for循环的几种写法7.1、基本用…

Spring的事务管理

tx标签用于配置事务管理用于声明和配置事务的相关属性 transaction-manager指定一个事务管理器的引用&#xff0c;用于管理事务的生命周期。propagation指定事务的传播属性&#xff0c;决定了在嵌套事务中如何处理事务。isolation指定事务的隔离级别&#xff0c;用于控制事务之…

华为新手机和支付宝碰一下 带来更便捷支付体验

支付正在变的更简单。 11月26日&#xff0c;华为新品发布会引起众多关注。发布会上&#xff0c;华为常务董事余承东专门提到&#xff0c;华为Mate 70和Mate X6折叠屏手机的“独门支付秘技”——“碰一下”&#xff0c;并且表示经过华为和支付宝的共同优化&#xff0c;使用“碰…

ADS学习笔记 7. 超外差接收机设计

基于ADS2023 update2 更多ADS学习笔记&#xff1a;ADS学习笔记 1. 功率放大器设计ADS学习笔记 2. 低噪声放大器设计ADS学习笔记 3. 功分器设计ADS学习笔记 4. 微带分支定向耦合器设计ADS学习笔记 5. 微带天线设计ADS学习笔记 6. 射频发射机设计 目录 -1、射频接收机性能指标…

蓝牙定位的MATLAB程序,四个锚点、三维空间

这段代码通过RSSI信号强度实现了在三维空间中的蓝牙定位&#xff0c;展示了如何使用锚点位置和测量的信号强度来估计未知点的位置。代码涉及信号衰减模型、距离计算和最小二乘法估计等基本概念&#xff0c;并通过三维可视化展示了真实位置与估计位置的关系。 目录 程序描述 运…

Linux命令进阶·如何切换root以及回退、sudo命令、用户/用户组管理,以及解决创建用户不显示问题和Ubuntu不显示用户名只显示“$“符号问题

目录 1. root用户&#xff08;超级管理员&#xff09; 1.1 用于账户切换的系统命令——su 1.2 退回上一个用户命令——exit 1.3 普通命令临时授权root身份执行——sudo 1.3.1 为普通用户配置sudo认证 2. 用户/用户组管理 2.1 用户组管理 2.2 用户管理 2.2.1 …

PDF版地形图矢量出现的问题

项目描述&#xff1a;已建风电场道路测绘项目&#xff0c;收集到的数据为PDF版本的地形图&#xff0c;图上标注了项目竣工时期的现状&#xff0c;之后项目对施工区域进行了复垦恢复地貌&#xff0c;现阶段需要准确的知道实际复垦修复之后的道路及其它临时用地的面积 解决方法&…

群论入门笔记

群的基本定义 群由一组元素 G 和一个运算&#xff08;常用符号包括 &#xff0c;x , 或 ∗&#xff09;组成。 封闭性 对于任意两个元素 x,y∈G&#xff0c;运算 x * y 的结果仍然属于集合 G&#xff0c;即&#xff1a; ∀x,y∈G,x∗y∈G. 结合律 对于任意 a,b,c∈G&…

LabVIEW内燃机气道试验台测控系统

基于LabVIEW软件开发的内燃机气道试验台测控系统主要应用于内燃机气道的性能测试和数据分析&#xff0c;通过高精度的测控技术&#xff0c;有效提升内燃机的测试精度和数据处理能力。 项目背景 随着内燃机技术的发展&#xff0c;对其气道性能的精准测量需求日益增加。该系统通…

LabVIEW将TXT文本转换为CSV格式(多行多列)

在LabVIEW中&#xff0c;将TXT格式的文本文件内容转换为Excel格式&#xff08;即CSV文件&#xff09;是一项常见的数据处理任务&#xff0c;适用于将以制表符、空格或其他分隔符分隔的数据格式化为可用于电子表格分析的形式。以下是将TXT文件转换为Excel&#xff08;CSV&#x…

第二节——计算机网络(四)物理层

车载以太网采用差分双绞线车载以太网并未指定特定的连接器&#xff0c;连接方式更为灵活小巧&#xff0c;能够大大减轻线束重量。传统以太网一般使用RJ45连接器连接。车载以太网物理层需满足车载环境下更为严格的EMC要求&#xff0c;100BASE-T1\1000BASE-T1对于非屏蔽双绞线的传…

Next.js - app 路由器之动态路由与并行路由

#题引&#xff1a;我认为跟着官方文档学习不会走歪路 动态路由的约定 (1) 通过将文件夹名称用方括号括起来可以创建动态段&#xff1a;[folderName] 动态段会作为 params 属性传递给 layout、page、route 和 generateMetadata 函数。 例如&#xff0c;一个博客可以包含以下…