欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > 用 React 编写一个笔记应用程序

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

2025/2/23 4:01:49 来源:https://blog.csdn.net/weixin_57266891/article/details/144137391  浏览:    关键词:用 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 ? (<Splitsizes={[30, 70]}direction="horizontal"className="split"><Sidebarnotes={notes}currentNote={findCurrentNote()}setCurrentNoteId={setCurrentNoteId}newNote={createNewNote}/>{currentNoteId && notes.length > 0 && (<EditorcurrentNote={findCurrentNote()}updateNote={updateNote}/>)}</Split>) : (<div className="no-notes"><h1>You have no notes</h1><buttonclassName="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}><divclassName={`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"><ReactMdevalue={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)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词