使用 React Flow 构建一个思维导图应用

242d9ec432ebcf91c101501536bfdf6b.jpeg

4f93d83082235977d4242b2bd3062fb0.jpeg

思维导图是围绕共同主题或问题将思想、概念、信息或任务分组的视觉表示。思维导图应用是一种软件应用,允许您创建、可视化和组织您的思想、想法和信息作为思维导图。本文将向您展示如何实现自己的思维导图应用程序。

在我们开始之前,我想向您展示一下我们在本教程结束时将拥有的思维导图应用程序

66c02940210259423ed562e03e1a0017.png

React Flow是什么?

React Flow是一个开源工具包,用于在React应用程序中生成交互式图表、流程图和可视化。它提供了一种强大的方式来创建和管理复杂的可视化,如思维导图、网络图和组织结构图等。React Flow基于React构建,并使用现代Web技术提供统一的用户体验。

成千上万的用户使用React Flow,从个人开源开发者到像Stripe和Typeform这样的大公司。该库已被用于数据处理工具、聊天机器人构建器、机器学习、音乐合成器和其他应用程序中。

选择一个满足你需求的库可能会很困难,因为在不断发展的行业中有太多的选择。然而,使用React Flow、思维导图以及规划和设计你的项目可能会简化这个过程,节省你的时间和烦恼。

尽管市场上有众多竞争对手,但React Flow作为最出色的思维导图和流程框架之一,仍然在大型项目的开发过程中持续为用户带来好处。

在本教程中,您将学习如何使用React Flow创建一个基本的思维导图应用程序,该应用程序可用于头脑风暴、构思想法或可视化思维。

项目设置

让我们从搭建我们的React应用开始。通过运行以下命令在您选择的目录中搭建React:

npm create vite@latest mind-mapping-app --template react

cd mind-mapping-app

Vite是一个为主要框架提供的工具,可以让您从基本模板快速启动项目。这些模板由社区维护,并针对各种框架或集成其他工具。其中一个模板可以使用类似Degit的工具来搭建您的项目。

npx degit user/project my-project
cd mind-mapping-app

npm install
npm run dev

创建组件

要创建一个组件,请在您的思维导图应用的src文件夹中导航并创建一个组件(component)文件夹。接下来,在组件文件夹中创建一个新文件, node.jsx 。

集成React Flow

将React Flow集成到您的Vite React项目中,请按照以下步骤进行操作:

首先,请确保您已经安装了React Flow。如果还没有安装,请运行以下命令:

npm install react-flow-renderer

接下来,导航到 node.jsx 目录并粘贴以下代码:

import React from "react";
import ReactFlow from "reactflow";

import "reactflow/dist/style.css";

const initialNodes = [
  {
    id: "1",
    type: "input",
    data: { label: "Mind Map" },
    position: { x: 0, y: 0 },
  },
];

export default function MindNode() {
  return (
    <div id="container">
      <ReactFlow nodes={initialNodes }></ReactFlow>
    </div>
  );
}

我们导入了项目所需的重要依赖项。然后,我们定义了一个名为 initialNodes 的数组。该数组包含了一个起始节点配置,每个节点都有几个属性。 MindNode 功能性的React组件返回 JSX ,用于渲染思维导图节点。

从那里,导航到 App.jsx 在 src 目录/文件夹中,并替换以下代码以渲染函数 MindNode 。

import React from "react";
import MindNode from "./components/node";

function App() {
  return (
    <div className="App">
      <MindNode />
    </div>
  );
}

export default App;

最后,在这个阶段,导航到 src/main.jsx 并粘贴下面的代码:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

上面的代码设置并渲染了你的主要React组件App到HTML DOM中。从目前的代码中,你应该得到下面所示的输出:

44a407d448d2f749f74a5e834cb253a0.gif

自定义节点外观

您可以通过修改React Flow应用程序中节点的外观,根据其类型或属性构建具有不同样式和视觉属性的节点。现在导航到 src/node.jsx 并将以下代码添加到其中:

import React, { useState } from "react";
import ReactFlow, { MiniMap, useNodesState } from "reactflow";

import "reactflow/dist/style.css";

const initialNodes = [
  {
    id: "1",
    type: "input",
    data: { label: "Mind Map" },
    position: { x: 0, y: 0 },
  },
];


export default function MindNode() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [name, setName] = useState("");

  const addNode = () => {
    setNodes((e) =>
      e.concat({
        id: (e.length + 1).toString(),
        data: { label: `${name}` },
        position: {
          x: Math.random() * window.innerWidth,
          y: Math.random() * window.innerHeight,
        },
      })
    );
  };

  return (
    <div id="container">
      <ReactFlow nodes={nodes} onNodesChange={onNodesChange}>
        <MiniMap
          nodeColor={(n) => {
            if (n.type === "input") return "blue";

            return "#FFCC00";
          }}
        />
      </ReactFlow>
      <div>
        <input
          type="text"
          onChange={(e) => setName(e.target.value)}
          name="title"
        />
        <button type="button" onClick={addNode}>
          Add Node
        </button>
      </div>
    </div>
  );
}

上面的代码创建了一个基本的思维导图应用程序。用户可以向地图添加自定义标签,并根据节点的类型改变其外观。Reactflow库包含处理思维导图状态和交互性所需的组件和钩子。 miniMap 允许您从小的视角看到整个屏幕。

添加互动性

93c4d4105d9c1d73dffa25c6b4b732c8.gif

从上面的片段中我们可以看到,我们无法连接节点。允许用户与节点和边进行交互,比如建立连接和与节点进行交互,是为思维导图应用程序增加互动性的一种方式。请将以下代码复制并粘贴到您的 src/node.jsx 中。

import React, { useState, useCallback } from "react";
import ReactFlow, {
  MiniMap,
  Controls,
  useNodesState,
  useEdgesState,
  addEdge,
} from "reactflow";

import "reactflow/dist/style.css";

const initialNodes = [
  {
    id: "1",
    type: "input",
    data: { label: "Mind Map" },
    position: { x: 0, y: 0 },
  },
];
const initialEdges = [];
const onLoad = (reactFlowInstance) => {
  reactFlowInstance.fitView();
};

export default function MindNode() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const [name, setName] = useState("");

  const addNode = () => {
    setNodes((e) =>
      e.concat({
        id: (e.length + 1).toString(),
        data: { label: `${name}` },
        position: {
          x: Math.random() * window.innerWidth,
          y: Math.random() * window.innerHeight,
        },
      })
    );
  };

  const onConnect = useCallback(
    (params) => setEdges((eds) => addEdge(params, eds)),
    [setEdges]
  );

  return (
    <div id="container">
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        onLoad={onLoad}
      >
        <Controls />

        <MiniMap
          nodeColor={(n) => {
            if (n.type === "input") return "blue";

            return "#FFCC00";
          }}
        />
      </ReactFlow>
      <div>
        <input
          type="text"
          onChange={(e) => setName(e.target.value)}
          name="title"
        />
        <button type="button" onClick={addNode}>
          Add Node
        </button>
      </div>
    </div>
  );
}

上面的代码使用useEdgesState来管理边的状态,并使用useCallback定义onConnect来处理节点边(连接)的生成。之后,我们使用所需的边、事件处理程序和缩放和平移显示的控件来渲染一个ReactFlow组件。

保存和加载思维导图

在基于React Flow的应用中保存和加载思维导图是一个重要的功能,允许用户保存和恢复他们的工作。这个功能提高了您的应用的可用性和价值。

保存思维导图

保存思维导图时,您必须收集表示思维导图中节点和边的数据。这些信息应该被转换成可存储的格式,比如JSON。您可以利用元素的状态来捕捉思维导图的当前状态。导航到 src 文件夹并创建一个新文件 storage.jsx ,添加以下代码:

export const saveMindMap = (nodes, edges) => {
  const data = { nodes, edges };
  localStorage.setItem("mindMapData", JSON.stringify(data));
};

以上代码定义了 saveMindMap 函数,该函数将思维导图的数据,包括节点和边的信息,保存到浏览器的本地存储中。本地存储是一种在用户设备上存储少量数据的简单方法。

加载思维导图:

加载思维导图与保存相反。您获取保存的数据,反序列化它,然后使用加载的数据更新React Flow画布。要加载保存的思维导图,请将以下代码粘贴到您的 src/storage.jsx 中。

export const loadMindMap = () => {
  const data = localStorage.getItem("mindMapData");
  return data ? JSON.parse(data) : null;
};

上面的代码从本地存储中检索序列化数据,将其解析为对象并返回该对象。接下来,将以下函数导入到您的组件中,并使用它们来保存和加载思维导图:

import React, { useState, useCallback, useEffect } from "react";
import ReactFlow, {
  MiniMap,
  Controls,
  useNodesState,
  useEdgesState,
  addEdge,
  Background,
} from "reactflow";
import { saveMindMap, loadMindMap } from "./Storage";
import "reactflow/dist/style.css";

const initialNodes = [
  {
    id: "1",
    type: "input",
    data: { label: "Mind Map" },
    position: { x: 0, y: 0 },
    style: { border: "20px solid #9999" },
  },
];
const initialEdges = [];
const onLoad = (reactFlowInstance) => {
  reactFlowInstance.fitView();
};

export default function MindNode() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const [name, setName] = useState("");

  const addNode = () => {
    setNodes((e) =>
      e.concat({
        id: (e.length + 1).toString(),
        data: { label: `${name}` },
        position: {
          x: Math.random() * window.innerWidth,
          y: Math.random() * window.innerHeight,
        },
        style: { border: "10px solid #9999" },
      })
    );
  };

  const onConnect = useCallback(
    (params) => setEdges((eds) => addEdge(params, eds)),
    [setEdges]
  );
  const handleSaveClick = () => {
    saveMindMap(nodes, edges);
    console.log(nodes);
  };
  const handleLoadClick = () => {
    const loadedData = loadMindMap();
    if (loadedData) {
      setNodes(loadedData.nodes);
      setEdges(loadedData.edges);
      console.log(loadedData);
    }
  };

  const refreshPage = () => {
    window.location.reload();
  };
  // const nodeOrigin = [0.5, 0.5];
  const connectionLineStyle = {
    stroke: "#9999",
    strokeWidth: 3,
    
  };
  const defaultEdgeOptions = { style: connectionLineStyle, type: "mindmap" };

  return (
    <div id="container">
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        connectionLineStyle={connectionLineStyle}
        defaultEdgeOptions={defaultEdgeOptions}
        onConnect={onConnect}
        onLoad={onLoad}
      >
        <Controls />
        <Background variant="dots" gap={12} size={1} />
        <MiniMap
          nodeColor={(n) => {
            if (n.type === "input") return "blue";

            return "#FFCC00";
          }}
        />
      </ReactFlow>
      <div>
        <input
          type="text"
          onChange={(e) => setName(e.target.value)}
          name="title"
        />
        <button id="one" type="button" onClick={addNode}>
          Add Node
        </button>
      </div>
      <div>
        <button id="two" onClick={handleSaveClick}>
          Save Mind Map
        </button>
        <button id="three" onClick={handleLoadClick}>
          Load Mind Map
        </button>
        <button id="four" onClick={refreshPage}>
          Refresh
        </button>
      </div>
    </div>
  );
}

通过这些更改,您现在有一个用于保存当前思维导图数据的 handleSaveClick 函数,一个用于加载先前保存的思维导图数据的 handleLoadClick 函数,以及一个用于重新加载页面到其原始形式的 refreshPage 函数。

界面设计的样式化

我们的开发过程几乎完成了。让我们根据我们的特定需求来定制我们的设计。导航到 src/index.css 并替换以下代码:

body {
  margin: 0;
  background-color: #f8f8f8;
}
#container {
  height: 90vh;
  width: 100%;
}
input {
  border: 1px solid rgb(203, 186, 186);
  padding: 4px 8px;
  border-radius: 5px;
  font-weight: 700;
  background: transparent;
  height: 100%;
  color: #222;
}

input:focus {
  border: none;
  outline: none;
  background: rgba(255, 255, 255, 0.25);
  pointer-events: all;
}
button {
  padding: 6px 8px;
  border-radius: 5px;
  border: none;
  background-color: #008cba;
  color: white;
}
button:hover {
  background-color: rgb(69, 69, 229);
}
input[type="text"] {
  padding: 8px;
  margin-right: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.react-flow {
  border: 3px solid #9999;
  border-radius: 10px;
}
/* .react-flow_nodes {
  color: #a02828;
} */
#two {
  background-color: #4caf50;
  margin: 5px;
}
#two:hover {
  background-color: #63c266;
}
#three {
  background-color: #f44336;
  margin: 2px;
}
#three:hover {
  background-color: rgb(190, 68, 68);
}
#four {
  background-color: rgb(86, 86, 6);
}
#four:hover {
  background-color: rgb(113,

测试

我们将进行一项测试,以了解我们的ReactFlow Mind应用程序的工作原理。

79b6ce50b1bc19cd018cc99a098b2bd3.png

请注意:您可以连接不同位置的节点,并且您还可以为每个节点指定一个期望的名称,以说明您的想法或项目。

结束

使用React Flow创建一个思维导图应用是一个有趣且多功能的项目,可以根据不同的用例进行调整,从头脑风暴会议到项目管理等等。在本指南中,我们已经涵盖了构建一个可工作的思维导图应用的重要步骤,例如设置开发环境,集成React Flow,修改节点外观,添加交互,并实现保存、加载和刷新功能。您可以根据需要添加更多功能和功能。

由于文章内容篇幅有限,今天的内容就分享到这里,文章结尾,我想提醒您,文章的创作不易,如果您喜欢我的分享,请别忘了点赞和转发,让更多有需要的人看到。同时,如果您想获取更多前端技术的知识,欢迎关注我,您的支持将是我分享最大的动力。我会持续输出更多内容,敬请期待。

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

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

相关文章

【C++11】多线程库 {thread线程库,mutex互斥锁库,condition_variable条件变量库,atomic原子操作库}

在C11之前&#xff0c;涉及到多线程问题&#xff0c;都是和平台相关的&#xff0c;比如windows和linux下各有自己的接口&#xff0c;这使得代码的可移植性比较差。 //在C98标准下&#xff0c;实现可移植的多线程程序 —— 条件编译 #ifdef _WIN32CreateThread(); //在windows系…

【C++】容器string的构造函数和迭代器

目录 1 什么是string 2 迭代器 2.1 迭代器的使用 2.2 auto关键字以及代码可读性 3 构造函数 1 什么是string 上图是C官网对于string的解释&#xff1a;string是表示字符序列的对象。 string本质是一个类&#xff0c;位于std的命名空间下&#xff0c;使用前需要包含头文件…

十三.日志包设计

十三.日志包设计 一.前言 在 Go 语言项目中自己设计日志包是非常重要的&#xff0c;原因如下&#xff1a;提高代码可读性和可维护性&#xff1a;良好的日志设计可以让代码更加易读和易于维护。日志可以帮助开发人员理解代码的运行过程&#xff0c;方便调试和错误排查。支持调…

C语言二进制数(ZZULIOJ1068:二进制数)

题目描述 将一个二进制数&#xff0c;转换为对应的十进制数。 输入&#xff1a;输入一个只含有’0’和’1’的字符串&#xff0c;以回车结束&#xff0c;表示一个二进制数。该二进制数无符号位&#xff0c;长度不超过31。 输出&#xff1a;输出一个整数&#xff0c;为该二进制数…

实在智能携手中国电信翼支付,全球首款Agent智能体亮相2023数字科技生态大会

11月10日-13日&#xff0c;中国电信与广东省人民政府联合主办的“2023数字科技生态大会”在广州隆重举行。本届大会以“数字科技焕新启航”为主题&#xff0c;邀请众多生态合作伙伴全方位展示数字科技新成果&#xff0c;包括数字新消费、产业数字化、智能电子、人工智能大模型等…

阿里云服务器 手动搭建WordPress(CentOS 8)

前提条件 已创建Linux操作系统的ECS实例&#xff0c;并且手动部署LNMP环境&#xff0c;具体操作&#xff0c;请参见手动部署LNMP环境&#xff08;CentOS 8&#xff09;。本教程使用的相关资源版本如下。 实例规格&#xff1a;ecs.c6.large 操作系统&#xff1a;公共镜像CentO…

【C++初阶】内存管理 初识模板

目录 一、C/C内存分布二、C/C动态内存管理方式2.1 new和delete的用法2.2 new与malloc、delete与free比较2.3 较复杂场景分析 三、operator new与operator delete函数四、 new和delete的实现原理五、初识模板5.1 泛型编程5.2 函数模板5.2.1 概念5.2.2 写法5.2.3 不同类型时使用函…

excel怎么能锁住行 和/或 列的自增长,保证粘贴公式的时候不自增长或者只有部分自增长

例如在C4单元格中输入了公式&#xff1a; 现在如果把C4拷贝到C5&#xff0c;D3会自增长为D4&#xff1a; 现在如果想拷贝的时候不自增长&#xff0c;可以先把光标放到C4单元格&#xff0c;然后按F4键&#xff0c;行和列的前面加上了$符号&#xff0c;锁定了&#xff1a; …

MySQL优化(1):B+树与索引

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 对于60%的程序员而言&a…

【智能家居】4、智能家居框架设计和代码文件工程建立

目录 一、智能家居项目框架 二、智能家居工厂模式示意 三、代码文件工程建立 SourceInsight创建新工程步骤 一、智能家居项目框架 二、智能家居工厂模式示意 三、代码文件工程建立 创建一个名为si的文件夹用于保存SourceInsight生成的文件信息&#xff0c;然后在SourceInsig…

.Net6 部署到IIS示例

基于FastEndpoints.Net6 框架部署到IIS 环境下载与安装IIS启用与配置访问网站 环境下载与安装 首先下载环境安装程序&#xff0c;如下图所示,根据系统位数选择x86或者x64进行下载安装,网址&#xff1a;Download .NET 6.0。 IIS启用与配置 启用IIS服务 打开控制面板&#xff…

学习css过渡动画-transition

文章目录 前言transition属性语法宽度改变效果透明度改变效果位置改变效果如有启发&#xff0c;可点赞收藏哟~ 前言 通常&#xff0c;当一个元素的样式属性值发生变化时&#xff0c;会立即看到页面发生变化。 css属性transition能让页面元素不是立即的、而是慢慢的从一种状态变…

【Proteus仿真】【STM32单片机】公交车报站系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用LCD12864显示模块、DS18B20温度传感器、DS1302时钟模块、按键、LED蜂鸣器、ULN2003、28BYJ48步进电机模块等。 主要功能&#xff1a; 系统运行…

2022年6月 电子学会青少年软件编程 中小学生Python编程 等级考试一级真题答案解析(选择题)

2022年6月Python编程等级考试一级真题解析 选择题(共25题,每题2分,共50分) 1、在Python编辑器中写好程序代码后,在Run菜单中,下列哪个命令可以用来执行程序 A、Check Module B、Run Module C、Python shell D、任意一个都可以 答案:B 考点分析:考查python编辑的…

表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信你对这篇博客也感兴趣o (ˉ▽ˉ&#xff1b;) &#x1f4dc;表白墙/留言墙初级Spring Boot项目&#xff08;此篇博客的简略版&#xff0c;不带MyBatis数据库开发&#xff09; 目录 1、项目前端页面及项目…

ER 图是什么

文章目录 前言什么是 ER图ER 图实例简化的 ER 图总结 前言 产品经理在梳理产业业务逻辑的过程中&#xff0c;非常重要的一项工作就是梳理各个业务对象之间的关系。如果涉及对象很对的时候&#xff0c;没有工具支持的话很难处理清楚。今天我们就来介绍一个梳理业务对象关系的工…

前置语音群呼与语音机器人群呼哪个更好

最近通过观察自己接到的营销电话&#xff0c;通过语音机器人外呼的量应该有所下降。同时和客户交流获取到的信息&#xff0c;也是和这个情况类似&#xff0c;很多AI机器人群呼的量转向了OKCC前置语音群呼。询问原因&#xff0c;说是前置语音群呼转化更快&#xff0c;AI机器人群…

头歌 MySQL数据库 - 初识MySQL

本章内容是为了完成老师布置的作业&#xff0c;同时也是为了以后考试的时候方便复习。 数据库部分一条一条的写&#xff0c;可鼠标手动粘贴&#xff0c;除特定命令外未分大小写。 第1关&#xff1a;创建数据库 在操作数据库之前&#xff0c;需要连接它&#xff0c;输入命令&a…

《Deep learning for fine-grained image analysis: A survey》阅读笔记

论文标题 《Deep learning for fine-grained image analysis: A survey》 作者 魏秀参&#xff0c;旷世研究院 初读 摘要 细粒度图像分析&#xff08;FGIA&#xff09;的任务是分析从属类别的视觉对象。 细粒度性质引起的类间小变化和类内大变化使其成为一个具有挑战性的…

一起学docker系列之五docker的常用命令--操作容器的命令

目录 前言1 启动容器2 查看容器3 退出容器4 启动已经停止的容器5 重启容器6 停止容器7 删除已经停止的容器8 启动容器说明和举例9 查看容器日志10 查看容器内运行的进程11 查看容器内部细节12 进入正在运行的容器并进行交互13 导入和导出容器结语 前言 当涉及到容器化技术&…