<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Fupeng Wang</title>
    <description>The latest articles on Forem by Fupeng Wang (@fupeng_wang).</description>
    <link>https://forem.com/fupeng_wang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2545739%2Fee1045d3-3715-4365-a2ea-2ff3bda1536c.PNG</url>
      <title>Forem: Fupeng Wang</title>
      <link>https://forem.com/fupeng_wang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fupeng_wang"/>
    <language>en</language>
    <item>
      <title>React+Vitest to write testable components, common problems record.</title>
      <dc:creator>Fupeng Wang</dc:creator>
      <pubDate>Mon, 30 Dec 2024 03:31:46 +0000</pubDate>
      <link>https://forem.com/fupeng_wang/reactvitest-to-write-testable-components-common-problems-record-1fdc</link>
      <guid>https://forem.com/fupeng_wang/reactvitest-to-write-testable-components-common-problems-record-1fdc</guid>
      <description>&lt;p&gt;Hi there, my name is Fupeng Wang.&lt;/p&gt;

&lt;p&gt;I am a senior full-stack engineer, and author of a &lt;a href="https://www.wangeditor.com/en/" rel="noopener noreferrer"&gt;17.5k open-source project&lt;/a&gt;, PMP. Now I am developing a Notion-style knowledge base &lt;br&gt;
&lt;a href="https://www.huashuiai.com/en" rel="noopener noreferrer"&gt;HuashuiAI&lt;/a&gt; including AI writing and collaboration, using React Nextjs and Supabase.&lt;/p&gt;

&lt;p&gt;In this article, I will share how React + Vitest to write testable components, and record some common problems I encountered&lt;/p&gt;
&lt;h2&gt;
  
  
  i18n
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.huashuiai.com/en" rel="noopener noreferrer"&gt;HuashuiAI&lt;/a&gt; project supports i18n international multilingual development using &lt;a href="https://next-intl.dev/" rel="noopener noreferrer"&gt;next-intl&lt;/a&gt;, I will share it the next articles.&lt;/p&gt;

&lt;p&gt;When conducting component unit testing, it is necessary to simulate the use of next-intl and wrap the target component with a provider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3w7b7eunxrohzwbqfyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3w7b7eunxrohzwbqfyh.png" alt="Image description" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This outer provider component requires passing in &lt;code&gt;message&lt;/code&gt; and &lt;code&gt;locale&lt;/code&gt;, as shown in the following figure&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nkstejqbebrlkk8chcu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nkstejqbebrlkk8chcu.png" alt="Image description" width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  DOM Events
&lt;/h2&gt;

&lt;p&gt;You can use &lt;code&gt;fireEvent&lt;/code&gt; in &lt;code&gt;@testing-library/react&lt;/code&gt; to simulate DOM events &lt;a href="https://testing-library.com/docs/dom-testing-library/api-events" rel="noopener noreferrer"&gt;https://testing-library.com/docs/dom-testing-library/api-events&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;click&lt;/code&gt; events, change events modify input values, and even simulate selecting files&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfo0qc5d5hn7j1cly4b1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfo0qc5d5hn7j1cly4b1.png" alt="Image description" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;waitFor&lt;/code&gt; to handle asynchronous processing &lt;a href="https://testing-library.com/docs/dom-testing-library/api-async#waitfor" rel="noopener noreferrer"&gt;https://testing-library.com/docs/dom-testing-library/api-async#waitfor&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Mock Global Variables
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;vi.stubGlobal&lt;/code&gt; in Vitest can simulate global variables and APIs &lt;a href="https://vitest.dev/api/vi.html#vi-stubglobal" rel="noopener noreferrer"&gt;https://vitest.dev/api/vi.html#vi-stubglobal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwj8357s1n5m2blukiko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwj8357s1n5m2blukiko.png" alt="Image description" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also common to use &lt;code&gt;vi.fn()&lt;/code&gt; to simulate functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7tdpq21sx8ljzkpudl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7tdpq21sx8ljzkpudl9.png" alt="Image description" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Mock API Request
&lt;/h2&gt;

&lt;p&gt;Vitest cannot directly simulate APIs, but it provides other ways in the documentation &lt;a href="https://vitest.dev/guide/mocking#requests" rel="noopener noreferrer"&gt;https://vitest.dev/guide/mocking#requests&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the reference document, we need to install &lt;code&gt;MSW&lt;/code&gt; first. Please refer to the MSW document for details &lt;a href="https://link.juejin.cn/?target=https%3A%2F%2Fmswjs.io%2Fdocs%2Fgetting-started" rel="noopener noreferrer"&gt;https://link.juejin.cn/?target=https%3A%2F%2Fmswjs.io%2Fdocs%2Fgetting-started&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install msw@latest --save-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we mock API &lt;code&gt;/api/user&lt;/code&gt; and return &lt;code&gt;{ errno: 0 }&lt;/code&gt;, as shown in the following figure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3h5rne9pogaf3unztpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3h5rne9pogaf3unztpv.png" alt="Image description" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Zustand
&lt;/h2&gt;

&lt;p&gt;When using Zustand &lt;code&gt;store&lt;/code&gt; global variables in React components, it is necessary to initialize these data first before rendering the correct DOM structure.&lt;/p&gt;

&lt;p&gt;The code is shown in the following figure. In the &lt;code&gt;beforeAll&lt;/code&gt; or &lt;code&gt;beforeEach&lt;/code&gt; hook function, simply initialize with &lt;code&gt;setState&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qhuttdxspaosq0w39ai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qhuttdxspaosq0w39ai.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Compatible with Nextjs Router
&lt;/h2&gt;

&lt;p&gt;When some components use &lt;code&gt;useRouter&lt;/code&gt; internally, for examples&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygtccya5t1o2sdxl4gnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygtccya5t1o2sdxl4gnw.png" alt="Image description" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When testing this component, a very simple test code reported an error at &lt;code&gt;useRouter()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1azyq9hnqy5icp4c7fd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1azyq9hnqy5icp4c7fd8.png" alt="Image description" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to install &lt;code&gt;next-router-mock&lt;/code&gt; to mock router.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install next-router-mock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then we have to create a &lt;code&gt;vitestSetup.ts&lt;/code&gt; to config it, and consider &lt;code&gt;next/router&lt;/code&gt; and &lt;code&gt;@/i18n/routing&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5duyo5gm8703ftbiyrg6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5duyo5gm8703ftbiyrg6.png" alt="Image description" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Actions
&lt;/h2&gt;

&lt;p&gt;Unit testing has been configured into the GitHub action process, and code testing is automatically executed every time code is submitted to ensure code quality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiz4p5iay4kc2cfk6ter4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiz4p5iay4kc2cfk6ter4.png" alt="Image description" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the way, I am looking for an international job opportunity, if you have a chance, welcome to connect me on my &lt;a href="https://github.com/wangfupeng1988" rel="noopener noreferrer"&gt;Github profile&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>vitest</category>
      <category>nextjs</category>
      <category>testing</category>
    </item>
    <item>
      <title>How can VoidZero be commercialized?</title>
      <dc:creator>Fupeng Wang</dc:creator>
      <pubDate>Mon, 23 Dec 2024 07:08:30 +0000</pubDate>
      <link>https://forem.com/fupeng_wang/how-can-voidzero-be-commercialized-669</link>
      <guid>https://forem.com/fupeng_wang/how-can-voidzero-be-commercialized-669</guid>
      <description>&lt;p&gt;Hi there, my name is Fupeng Wang.&lt;/p&gt;

&lt;p&gt;I am a senior full-stack engineer, and author of a &lt;a href="https://www.wangeditor.com/en/" rel="noopener noreferrer"&gt;17.5k open-source project&lt;/a&gt;, PMP. Now I am developing a Notion-style knowledge base &lt;br&gt;
&lt;a href="https://www.huashuiai.com/en" rel="noopener noreferrer"&gt;HuashuiAI&lt;/a&gt; including AI writing and collaboration, using React Nextjs and Supabase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In October of this year, Vue author Evan You announced the establishment of VoiceZero, aiming to solve problems such as fragmented JS toolchains, incompatibility, and low efficiency. VoiceZero has received millions of dollars in investment and now has products under development, Rolldown and Oxc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0pp3xzwgogsq8lskum9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0pp3xzwgogsq8lskum9.png" alt="Image description" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this reason, two weeks ago I wrote an article &lt;a href="https://dev.to/fupeng_wang/what-is-evan-you-doing-by-creating-voidzero-and-what-are-the-issues-with-js-toolchains-149p"&gt;What is Evan You doing by creating VoidZero, and what are the issues with JS toolchains?&lt;/a&gt; Analyzed the current situation and problems of front-end JS toolchains.&lt;/p&gt;

&lt;p&gt;But there is another question that everyone has been discussing, a very practical one: how can VoidZero make a profit? Investment inevitably requires returns, and capital is profit driven.&lt;/p&gt;

&lt;p&gt;How can we make money? &lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; can't be paid to use it, can we. Relying on community sponsorship? Pull it down, this basic tool relies on sponsorship and can refer to the story of the core-js author.&lt;/p&gt;

&lt;h2&gt;
  
  
  Author's explanation
&lt;/h2&gt;

&lt;p&gt;Someone replied below the article just now, citing the words of Vue author Evan You himself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;End to end services for enterprises......&lt;/li&gt;
&lt;li&gt;Differentiated licensing model......&lt;/li&gt;
&lt;li&gt;Product matrix and traffic guidance......&lt;/li&gt;
&lt;li&gt;Ecosystem building and partnership relationships......&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also found a tweet from the author, which was also quite vague. I completely understand this. Talking about money hurts emotions, talking about technology in a high-profile manner, and making money requires being low-key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd88uth2e6yjcqys7vjoj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd88uth2e6yjcqys7vjoj.png" alt="Image description" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of these are correct, but saying it means not saying anything, and after reading it, I don't know anything. Just like how Web3 provides a formal explanation of ten thousand words and a layman's explanation of virtual currency.&lt;/p&gt;

&lt;h2&gt;
  
  
  What things are suitable for payment?
&lt;/h2&gt;

&lt;p&gt;Firstly, tools such as &lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; and &lt;a href="https://swc.rs/" rel="noopener noreferrer"&gt;SWC&lt;/a&gt; cannot be used for a fee. There are too many products of the same type, so if you charge, everyone won't need to use them. Of course, being free does not mean it has no value. With more users, there is a great potential for natural value.&lt;/p&gt;

&lt;p&gt;There are paid tools available. For example, the &lt;a href="https://tiptap.dev/" rel="noopener noreferrer"&gt;Tiptap&lt;/a&gt; rich text editor is free to use, but some advanced features and APIs are paid for. For example, the &lt;a href="https://www.cursor.com/" rel="noopener noreferrer"&gt;cursor&lt;/a&gt; editor heavily relies on AI capabilities, so it requires payment for use.&lt;/p&gt;

&lt;p&gt;Most AI related development tools and services are paid for, such as &lt;a href="https://v0.dev/" rel="noopener noreferrer"&gt;v0&lt;/a&gt; and &lt;a href="https://bolt.new/" rel="noopener noreferrer"&gt;bolt&lt;/a&gt;, as well as &lt;a href="https://www.notion.com/" rel="noopener noreferrer"&gt;Notion&lt;/a&gt; AI. But these are not so closely related to Vite and VoidZero.&lt;/p&gt;

&lt;p&gt;In the fields of research and technology, the most common form of payment is online services such as servers, databases, storage, CDN, Edge. There are also some popular services, such as login verification, payment, email notifications, monitoring alarms, content review, etc.&lt;/p&gt;

&lt;p&gt;Large platforms such as Amazon,Google Cloud, Microsoft Azure, etc. There are also many small and medium-sized platforms, such as Vercel Supabase, Netlify, etc. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://deno.com/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt; received a $20M investment in 2022, how will it make a profit? It developed &lt;a href="https://deno.com/deploy" rel="noopener noreferrer"&gt;Deno Deploy&lt;/a&gt;, focusing on the &lt;a href="https://deno.com/blog/the-future-of-web-is-on-the-edge" rel="noopener noreferrer"&gt;Edge&lt;/a&gt; advantage, ultimately aiming to make money through online services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmow1si6ifj6mtj1tfv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmow1si6ifj6mtj1tfv8.png" alt="Image description" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As long as there is a deploy entry point, the subsequent database, storage, and CDN can be discussed. You can choose to integrate with other service providers or develop your own. &lt;/p&gt;

&lt;p&gt;But what about those small and medium-sized platforms without traffic? Is it possible to invest in Vite as an entry point? Vite has a very large user base, which is definitely a great idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyze the investors of VoiceZero
&lt;/h2&gt;

&lt;p&gt;Look at VoidZero's investors, StackBlitz Supabase Sentry Netlify Strikingly Prisma Resend, they are all fee based projects, covering various fields such as server, deployment, databases, third-party services, online monitoring, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiba3thdcoaig7pmqbijm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiba3thdcoaig7pmqbijm.png" alt="Image description" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They have competitors, how can they better acquire users and cultivate user habits? Vite has a massive user base, and as long as guidance is done well, it will bring huge traffic, which in turn will lead to a large number of paid upgrades.&lt;/p&gt;

&lt;p&gt;For example, if I want to integrate a service for sending emails in the project and use the Vite command to integrate Resend, do you want to use it or manually configure it yourself? Do you want to do project statistics and monitoring? Vite can integrate Sentry with just one click. Do you want to use it or do you need to configure other platforms yourself?&lt;/p&gt;

&lt;p&gt;For experienced coders, they may be able to configure these issues themselves, but for batches of newcomers, they will definitely choose the most direct way first, and gradually cultivate user habits.&lt;/p&gt;

&lt;p&gt;I guess that in the next 2 years, Vite or VoiceZero will definitely integrate deployment services, or even develop their own deployment services. They will earn intermediary fees.&lt;/p&gt;

&lt;p&gt;I really hope this model can work and explore a profitable path for open source projects!&lt;/p&gt;

&lt;h2&gt;
  
  
  The end
&lt;/h2&gt;

&lt;p&gt;There will always be problems, needs, and opportunities in this world.&lt;/p&gt;

&lt;p&gt;I remember when Gates said that 64MB of memory on a PC was perfect, and Jobs said that a 4-inch phone screen was perfect. Later on, there were also revolutionary changes.&lt;/p&gt;

&lt;p&gt;Be a thoughtful person, keep observing the world, broaden your horizons, life is about turmoil.&lt;/p&gt;

&lt;p&gt;By the way, I am looking for an international job opportunity, if you have a chance, welcome to connect me on my &lt;a href="https://github.com/wangfupeng1988" rel="noopener noreferrer"&gt;Github profile&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>vue</category>
      <category>vite</category>
    </item>
    <item>
      <title>What is Evan You doing by creating VoidZero, and what are the issues with JS toolchains?</title>
      <dc:creator>Fupeng Wang</dc:creator>
      <pubDate>Tue, 17 Dec 2024 07:59:25 +0000</pubDate>
      <link>https://forem.com/fupeng_wang/what-is-evan-you-doing-by-creating-voidzero-and-what-are-the-issues-with-js-toolchains-149p</link>
      <guid>https://forem.com/fupeng_wang/what-is-evan-you-doing-by-creating-voidzero-and-what-are-the-issues-with-js-toolchains-149p</guid>
      <description>&lt;p&gt;Hi there, my name is Fupeng Wang.&lt;/p&gt;

&lt;p&gt;I am a senior full-stack engineer, and author of a &lt;a href="https://www.wangeditor.com/en/" rel="noopener noreferrer"&gt;17.5k open-source project&lt;/a&gt;, PMP. Now I am developing a Notion-style knowledge base &lt;br&gt;
&lt;a href="https://www.huashuiai.com/en" rel="noopener noreferrer"&gt;HuashuiAI&lt;/a&gt; including AI writing and collaboration, using React Nextjs and Supabase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evan You and VoidZero
&lt;/h2&gt;

&lt;p&gt;Two months ago, Vue and Vite author Evan You &lt;a href="https://voidzero.dev/posts/announcing-voidzero-inc" rel="noopener noreferrer"&gt;announced&lt;/a&gt; the establishment of &lt;a href="https://voidzero.dev/" rel="noopener noreferrer"&gt;VoiceZero&lt;/a&gt; company and received millions of dollars in investment.&lt;/p&gt;

&lt;p&gt;VoidZero will be based on Vite and develop two major tools, Rolldown and Oxc, to unify the front-end JS toolchain. Resolve issues such as fragmentation, incompatibility, and low efficiency.&lt;/p&gt;

&lt;p&gt;The VoidZero core tool is developed in Rust language, with high running efficiency and fast speed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv13cmvv4jmc5ztt9plp1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv13cmvv4jmc5ztt9plp1.png" alt="Image description" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the issues with JS toolchains
&lt;/h2&gt;

&lt;p&gt;The JS toolchain mainly includes processes such as semantic analysis, transformer, linter, transformer, minifier, bounder, etc.&lt;/p&gt;

&lt;p&gt;How are we currently using these types of tools? What tools are currently available? What is the relationship between them? Are they really inefficient and fragmented? Let's introduce them one by one below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6l4yax04fkrvx4p7z74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6l4yax04fkrvx4p7z74.png" alt="Image description" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that their relationship is somewhat chaotic, with some being able to compile and package, and having a lot of overlapping functions, so there is no need to forcefully classify and compare them.&lt;/p&gt;

&lt;h2&gt;
  
  
  JS runtime
&lt;/h2&gt;

&lt;p&gt;The so-called runtime refers to the runtime environment of a language. If there is no runtime, then a language is just a string or string file that cannot be parsed and run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Nodejs&lt;/a&gt; is the most common JS runtime, which is stable, mature, and has a very rich ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deno.com/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt; is a JS runtime developed in recent years, focusing on TS support and network security. It received a $20M investment in the first two years and recently released Deno 2.0, which is currently developing rapidly&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun&lt;/a&gt; JS runtime， Focus on performance and all-in-one
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Nodejs Deno 或 Bun，都是写 JS 代码&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Bun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome to Bun!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Listening on localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: The JS runtime is not a part of the JS toolchain, it is just the most basic capability, but understanding these tools and terms can help distinguish them from the JS toolchain. You may not have used it, but you need to know its existence and what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parser/Compiler
&lt;/h2&gt;

&lt;p&gt;Web front-end development needs to consider compatibility with various browsers, as modern browsers are not yet able to directly run TS JSX and the latest ES code.&lt;/p&gt;

&lt;p&gt;So, we need to convert TS JSX ES and other code in the development environment into JS code that can be executed by the browser, usually ES5.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomlb4ct5e9bibslf07zf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomlb4ct5e9bibslf07zf.png" alt="Image description" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;Babel&lt;/a&gt; was one of the first to do this job, developed using JS, with a rich ecosystem and plugins, and has long been integrated into packaging tools such as webpack rollup.&lt;/p&gt;

&lt;p&gt;But Babel is developed in JS, so its running efficiency is relatively low. Moreover, Babel requires manual configuration when compiling TS JSX, which is quite cumbersome.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://swc.rs/" rel="noopener noreferrer"&gt;SWC&lt;/a&gt; is a JS compiler developed in Rust language, which is 20-70 times faster than Babel (on different CPUs), and natively supports TS and JSX syntax, aiming to replace Babel. Vite uses SWC internally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rspack.dev/" rel="noopener noreferrer"&gt;Rspack&lt;/a&gt; is developed using the Rust language, but it is not just a JS compiler. It is a comprehensive packaging tool with high efficiency.&lt;/p&gt;

&lt;p&gt;Like Rspack, &lt;a href="https://esbuild.github.io/" rel="noopener noreferrer"&gt;ESBullid&lt;/a&gt; is developed using the Go language and is a comprehensive packaging tool that includes a JS compiler, making it highly efficient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpx87aa8nf8rmh7iehk2n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpx87aa8nf8rmh7iehk2n.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Linter &amp;amp; Formatter
&lt;/h2&gt;

&lt;p&gt;The commonly used ones in this section are &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; and &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt;, but there are also some confusion in their usage&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are many configuration standards for ESLint (such as Recommended, Airbnb, Google, StandardJS, etc.), and everyone may use them differently. Different projects may also have different configurations&lt;/li&gt;
&lt;li&gt;ESLint and Prettier have some duplicated functions, making it difficult to choose when using them, and there may be duplicate configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrgicpz9zbfdew7xzj6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrgicpz9zbfdew7xzj6x.png" alt="Image description" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Minifier
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/uglify-js" rel="noopener noreferrer"&gt;UglifyJS&lt;/a&gt; was one of the first used to compress JS code, and now its download volume is also very large.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/terser" rel="noopener noreferrer"&gt;Terser&lt;/a&gt; is developed based on the UglifyJS source code, supporting ES6 new syntax and optimizing tree shaking.&lt;/p&gt;

&lt;p&gt;However, new tools such as &lt;a href="https://swc.rs/" rel="noopener noreferrer"&gt;SWC&lt;/a&gt; and &lt;a href="https://esbuild.github.io/" rel="noopener noreferrer"&gt;esbulid&lt;/a&gt; now also support JS code compression, and their execution efficiency is higher. After all, Rust or Go inherently have much higher execution efficiency than JS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boundler
&lt;/h2&gt;

&lt;p&gt;Packagers are the ones we most frequently come into contact with, such as &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; &lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; and &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;. The latter may not be commonly used, but it is also a well-established tool.&lt;/p&gt;

&lt;p&gt;Vite uses &lt;a href="https://swc.rs/" rel="noopener noreferrer"&gt;SWC&lt;/a&gt; as an interpreter, which is highly efficient. Use &lt;a href="https://esbuild.github.io/" rel="noopener noreferrer"&gt;esbulid&lt;/a&gt; for packaging in the development environment, and &lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;rollup&lt;/a&gt; for packaging in the production environment.&lt;/p&gt;

&lt;p&gt;Meanwhile, &lt;a href="https://esbuild.github.io/" rel="noopener noreferrer"&gt;esbulid&lt;/a&gt; (developed in Go language, as introduced earlier) and &lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;rollup&lt;/a&gt; can also be used separately as packaging tools, and many third-party JS plugins are packaged using rollup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://turbo.build/pack/docs" rel="noopener noreferrer"&gt;Turbopack&lt;/a&gt; is a JS packaging tool developed by &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; using Rust for &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; projects, and can also be used independently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rspack.dev/" rel="noopener noreferrer"&gt;Rspack&lt;/a&gt; is a JS packaging tool developed using Rust (along with a JS compiler), which can replace webpack and is very fast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdkfepgmngz1c5x5d5yu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdkfepgmngz1c5x5d5yu.png" alt="Image description" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://swc.rs/" rel="noopener noreferrer"&gt;SWC&lt;/a&gt; is also developing its own packaging tool swcpack - with so many, do you think it's messy? Is it fragmented?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwzohl2xkgpxybmdztty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwzohl2xkgpxybmdztty.png" alt="Image description" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use Rust
&lt;/h2&gt;

&lt;p&gt;You may be curious, why do so many tools need to be developed in Rust language? There are two main points&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rust is a compiled language that can be compiled into native code and run directly, with performance comparable to C++&lt;/li&gt;
&lt;li&gt;Rust memory management is safer, while C++ memory management is more prone to bugs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The End
&lt;/h2&gt;

&lt;p&gt;Looking back at three issues with JS toolchains&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fragmentation: This is too obvious, there are a lot of repetitive tools in various fields, especially boulders&lt;/li&gt;
&lt;li&gt;Incompatibility: Old tools have compatibility with new syntax, especially TS JSX, and require excessive configuration&lt;/li&gt;
&lt;li&gt;Low efficiency: This is also very obvious. In the tool field, JS cannot do as well as Rust, and it is only a matter of time before it is eliminated (don't panic: this is only the tool field, and the application field is still JS and TS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, Vue author Evan You has a sharp eye, looks very accurately, and can directly point to problems. Moreover, he has a strong advantage and grip in this part, which is Vite. Vite now has a large number of users and can serve as a good entry point.&lt;/p&gt;

&lt;p&gt;When web frameworks such as Vue and single tools such as Vite encounter bottlenecks in their development, Evan can break out of this circle, discover higher-level problems, and be able to execute them on the ground. This is something we should learn from.&lt;/p&gt;

&lt;p&gt;Finally, with so much investment, VoidZero needs a return on investment. How will it be commercialized in the future? It's impossible to charge Vite directly, so how can we make money?&lt;/p&gt;

&lt;p&gt;Follow me, I will analyze in detail in the next section. This will be more valuable than technology and code.&lt;/p&gt;

&lt;p&gt;By the way, I am looking for an international job opportunity, if you have a chance, welcome to connect me on my &lt;a href="https://github.com/wangfupeng1988" rel="noopener noreferrer"&gt;Github profile&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>vite</category>
      <category>webpack</category>
    </item>
    <item>
      <title>React + dnd-kit, implement tree-list drag and drop sortable</title>
      <dc:creator>Fupeng Wang</dc:creator>
      <pubDate>Tue, 10 Dec 2024 01:27:27 +0000</pubDate>
      <link>https://forem.com/fupeng_wang/react-dnd-kit-implement-tree-list-drag-and-drop-sortable-225l</link>
      <guid>https://forem.com/fupeng_wang/react-dnd-kit-implement-tree-list-drag-and-drop-sortable-225l</guid>
      <description>&lt;p&gt;Hi there, my name is Fupeng Wang.&lt;/p&gt;

&lt;p&gt;I am a senior full-stack engineer, and author of a &lt;a href="https://www.wangeditor.com/en/" rel="noopener noreferrer"&gt;17.5k open-source project&lt;/a&gt;, PMP. Now I am developing a Notion-style knowledge base &lt;br&gt;
&lt;a href="https://www.huashuiai.com/en" rel="noopener noreferrer"&gt;HuashuiAI&lt;/a&gt; including AI writing and collaboration, using React Nextjs and Supabase.&lt;/p&gt;

&lt;p&gt;In this article, I will share how to implement tree-list drag and drop sortable by React and dnd-kit. The source code link is at the bottom of this article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60aj65jy9v2rfloooho2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60aj65jy9v2rfloooho2.png" alt="tree-list sortable" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Dnd-kit and Sortable component
&lt;/h2&gt;

&lt;p&gt;Dnd-kit is a common drag-drop tool in the React ecosystem, and it supports &lt;a href="https://docs.dndkit.com/presets/sortable" rel="noopener noreferrer"&gt;sortable&lt;/a&gt; by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DndContext&lt;/span&gt; 
      &lt;span class="na"&gt;sensors&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sensors&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;collisionDetection&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;closestCenter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;onDragEnd&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleDragEnd&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SortableContext&lt;/span&gt; 
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;verticalListSortingStrategy&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SortableItem&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SortableContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DndContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it can only support the one-level list. If we want to implement a multi-level nested list (or tree), we have to customize it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define state date structure
&lt;/h2&gt;

&lt;p&gt;Modern front-end frameworks such as React Vue are data-driven views, so defining data structures first and then considering UI rendering.&lt;/p&gt;

&lt;p&gt;The most common data structure definition for multi-level nested lists (trees) is as follows, and virtual DOM vnode is also defined in this way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B2a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B2b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;E&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multi-level nested &lt;code&gt;SortableContext&lt;/code&gt; is not feasible
&lt;/h2&gt;

&lt;p&gt;Because the state data structure is nested, the first thing that comes to my mind is to nest and render the UI structure together.&lt;/p&gt;

&lt;p&gt;Firstly, nest &lt;code&gt;&amp;lt;SortableItem&amp;gt;&lt;/code&gt; within &lt;code&gt;&amp;lt;SortableContext&amp;gt;&lt;/code&gt; , which is the same method used for one-layer lists.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao5otg0g7f7l9kgq0bd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao5otg0g7f7l9kgq0bd6.png" alt="Image description" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, continue nesting the subordinate &lt;code&gt;&amp;lt;SortableContext&amp;gt;&lt;/code&gt; in &lt;code&gt;&amp;lt;SortableItem&amp;gt;&lt;/code&gt; to display children.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu223bt86i6i9570fwsfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu223bt86i6i9570fwsfr.png" alt="Image description" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The running effect is as follows. The problem is that drag and drop sorting is allowed within the same level, but cross level sorting is not possible because it is not a context - which is reasonable&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foie0lqpt0jvn0y1swsik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foie0lqpt0jvn0y1swsik.png" alt="Image description" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi level conversion to a single level is feasible
&lt;/h2&gt;

&lt;p&gt;Since nesting is not feasible, it is necessary to convert multiple levels into the single level.&lt;/p&gt;

&lt;p&gt;But it is necessary to add the &lt;code&gt;ancestorsIds&lt;/code&gt; attribute for each item, firstly to display the depth of the hierarchy, and secondly to know what parent nodes it has.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;ancestorIds&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IItem&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;IItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ancestorIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ancestorIds&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// add ancestorIds&lt;/span&gt;
      &lt;span class="p"&gt;}))&lt;/span&gt;
      &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rendering effect after conversion is as follows, and you can now drag and sort it. However, it will not take effect until the state sorting is modified.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg89kdk4ywo47jj99qs1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg89kdk4ywo47jj99qs1o.png" alt="Image description" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, we can also determine whether it can be moved through the hierarchical relationship of &lt;code&gt;ancestorsIDs&lt;/code&gt;. The parent node cannot be moved to its child nodes, otherwise the loop will be dead.&lt;/p&gt;

&lt;p&gt;For example, in the above figure, if we want to drag B2 to the position of B2a, we will find that the &lt;code&gt;ancestorsIDs&lt;/code&gt; of B2a contain B2. This is not possible because you cannot drag an item to its own subordinate. &lt;/p&gt;

&lt;h2&gt;
  
  
  Modify state data
&lt;/h2&gt;

&lt;p&gt;For ease of operation, the data is placed in the &lt;code&gt;Zustand&lt;/code&gt; global store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnt1g32tzwc0caoyppujt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnt1g32tzwc0caoyppujt.png" alt="Image description" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dnd-kit refers to the dragged element as an &lt;code&gt;activeItem&lt;/code&gt; and the placed target location as an &lt;code&gt;overItem&lt;/code&gt;. So modifying state data means moving &lt;code&gt;activeItem&lt;/code&gt; to the position of &lt;code&gt;overItem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If it is a single level, Dnd-kit provides a method &lt;code&gt;arrayMove&lt;/code&gt; that can be directly modified. The doc link &lt;a href="https://docs.dndkit.com/presets/sortable" rel="noopener noreferrer"&gt;https://docs.dndkit.com/presets/sortable&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxud4puwkfg5wid53r53n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxud4puwkfg5wid53r53n.png" alt="Image description" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But in multi-level nested lists (trees), you need to implement it yourself, which is a bit troublesome. The core code is here, and you can download the source code (at the end of the article) for reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jies2sg6sbxgnlwwroa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jies2sg6sbxgnlwwroa.png" alt="Image description" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Encountered a problem
&lt;/h2&gt;

&lt;p&gt;As shown in the figure below, when dragging A under B, A will move to the bottom of B as a whole, not inside B.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04u5fmpew2bf14vl4m4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04u5fmpew2bf14vl4m4x.png" alt="Image description" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To solve this problem, it is necessary to determine whether there are any child elements of B after B. If so, assign &lt;code&gt;overItem&lt;/code&gt; to its child elements&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8gnhmfohfs5vdx5ztxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8gnhmfohfs5vdx5ztxd.png" alt="Image description" width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then insert the current active element into the first element of items.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ldr8nalwzmda7bs50qz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ldr8nalwzmda7bs50qz.png" alt="Image description" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The end
&lt;/h2&gt;

&lt;p&gt;The source code link is here &lt;a href="https://github.com/wangfupeng1988/react-dnd-sortable-demo" rel="noopener noreferrer"&gt;https://github.com/wangfupeng1988/react-dnd-sortable-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the way, I am looking for an international job opportunity, if you have a chance, welcome to connect me on my &lt;a href="https://github.com/wangfupeng1988" rel="noopener noreferrer"&gt;Github profile&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>dnd</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
