npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-dom-transfer

v0.1.0

Published

Move DOM elements between containers without unmounting React components. Preserves video playback, iframe state, and all DOM state during transfer with optional FLIP animations.

Readme

react-dom-transfer

English

React 移动 DOM 必须销毁重建。这个库不用。

React 把元素移到新容器时,会先卸载再重建——过程中所有 DOM 状态全部丢失。iframe 重载、视频重播、Canvas 清空、滚动位置归零、焦点消失。

react-dom-transfer 趁 React 不注意,把真实 DOM 节点搬到另一个容器里。

  ┌─────────────┐                          ┌─────────────────────┐
  │  容器 A      │       transfer()         │  容器 B              │
  │ ┌─────────┐ │  ───────────────────────► │ ┌─────────────────┐ │
  │ │  元素    │ │    CSS 过渡 + FLIP 动画   │ │     元素          │ │
  │ └─────────┘ │                           │ │  (同一个实例)    │ │
  └─────────────┘                           │ └─────────────────┘ │
                        restore()           └─────────────────────┘
                  ◄───────────────────────

不卸载。不重建。状态不丢。可选 FLIP 过渡动画。

搬运过程中保留了什么

| DOM 状态 | 原生 appendChild | 本库 | |:--|:--:|:--:| | <video> 播放进度 | ❌ 从头开始 | ✅ 继续播放 | | <iframe> 已加载页面 | ❌ 重新加载 | ✅ 保持不动 | | <canvas> / WebGL 上下文 | ❌ 被清空 | ✅ 保留 | | 滚动位置 | ❌ 归零 | ✅ 保留 | | 输入框焦点和选区 | ❌ 丢失 | ✅ 保留 | | CSS 动画 / transition | ❌ 重置 | ✅ 继续 | | 事件监听器 | ❌ 可能失效 | ✅ 正常 | | React 组件 state | ✅(相同 key) | ✅ 始终保留 |

和现有方案的区别

| | 本库 | react-reparenting | react-reverse-portal | |:--|:--:|:--:|:--:| | 改 Fiber 树 | ❌ | ✅ | ❌ | | Context 保持 | ✅ | ❌ 会断 | ✅ | | Effects 重跑 | ❌ | ✅ 全部重跑 | ❌ | | 过渡动画 | ✅ 内建 | ❌ | ❌ | | 是否维护 | ✅ | ❌ 2021 停更 | ❌ 2021 停更 | | 核心思路 | 只换 stateNode | 重建 Fiber 链表 | Portal 目标切换 |

安装

npm install react-dom-transfer

快速上手

import { useDomTransfer } from 'react-dom-transfer';

function App() {
  const { sourceRef, targetRef, transfer, restore, isTransferred } =
    useDomTransfer({ transition: 'all 0.3s ease' });

  return (
    <>
      <div ref={sourceRef}>
        <iframe src="https://example.com" />
      </div>

      <div ref={targetRef} />

      <button onClick={isTransferred ? restore : transfer}>
        {isTransferred ? '收起' : '展开'}
      </button>
    </>
  );
}

<iframe> 会用 0.3 秒动画飞到目标容器。里面加载的页面不会重载。

命令式 API

不在 React 组件里也能用:

import { transferDom, restoreDom } from 'react-dom-transfer';

const state = transferDom(element, target, {
  transition: 'all 0.3s ease',
  onEnd: () => console.log('完成'),
});

// 之后
restoreDom(state);

原理

调用 transfer() 做了两件事:

1. FLIP 动画 — 记录位置 → 脱离容器 → position: fixed 定在原坐标 → CSS transition 飞到目标 → 落入目标容器。

2. Fiber stateNode 替换 — 把 __reactFiber$__reactProps$ 复制到搬运后的元素上,更新 fiber.stateNodefiber.alternate.stateNode。Fiber 树结构(child / sibling / return)完全不动。

React 看到的:    组件 → Fiber → stateNode → [同一个指针]
实际情况:        DOM 节点已经在另一个容器里了

这和 react-reparenting 的思路完全不同——后者修改 Fiber 树的链表结构(childsiblingreturn),导致 Context 变化、Effects 全部重跑。

API

useDomTransfer(options?)

const { sourceRef, targetRef, transfer, restore, isTransferred } =
  useDomTransfer({
    transition: 'all 0.3s ease', // CSS 过渡(可选,不传则瞬间完成)
    zIndex: 9999,                // 动画期间 z-index(默认 9999)
    timeout: 3000,               // 兜底超时 ms(默认 3000)
    onTransferEnd: () => {},     // 迁移完成后触发
    onRestoreEnd: () => {},      // 恢复完成后触发
  });

transferDom(element, target, options?) → state

restoreDom(state, options?)

实际应用场景

视频 / 直播

用户在 Feed 流里划动,每个卡片上有一个正在播放的小窗播放器。用户点击卡片 → 播放器用 0.3 秒动画飞入全屏详情页。流不重新缓冲,弹幕还在,音量不变,所有插件状态都在。用户点返回 → 播放器飞回 Feed 卡片。全程,<video> 元素是同一个 DOM 节点。

没有这个库:播放器销毁重建。流从 CDN 重新请求,用户看到 loading,开头 1-2 秒内容丢失。大规模场景(千万级用户)下,这种重复请求还会显著增加 CDN 成本。

嵌入内容(iframe)

一个 SaaS 产品通过 iframe 嵌入了第三方表单(Typeform、Google Forms、Stripe 支付)。表单一开始在侧边栏。用户点"展开" → iframe 移到全宽弹窗。用户填了一半表单,点"收起" → 回到侧边栏,已填内容还在。

没有这个库:每次布局切换 iframe 都重载。用户填的信息全部丢失。如果是支付流程(Stripe),意味着从头开始结账。

地图

一个房产网站在侧边栏显示小地图预览。用户点击地图 → 展开为全屏,卫星瓦片已经加载好了。用户浏览、缩放、标记。点"缩小" → 地图回到侧边栏,瓦片、缩放级别、标记全部保留。

没有这个库:Google Maps / Mapbox 重新初始化。所有瓦片重新下载(移动网络下很贵)。缩放级别重置,自定义图层和标记全没了。Maps SDK 重跑初始化,额外 200-500ms 卡顿。

富文本编辑器

一个类 Notion 的文档应用有分栏布局。用户把一个 Monaco / CodeMirror 编辑器块从左栏拖到右栏。光标位置、撤销/重做历史、语法高亮状态、未保存的修改全部保留。

没有这个库:编辑器重新挂载。撤销历史清空。如果用户正在编辑,内容可能闪烁或重置。Monaco 的语言服务重新初始化(大文件 200ms+)。

3D / WebGL

一个产品配置器用 Three.js 在卡片里展示 3D 模型。用户点"全屏" → canvas 展开,当前相机角度、已加载纹理、光照全部保留。用户旋转模型,点"退出全屏" → canvas 缩回,相机角度不变。

没有这个库:WebGL 上下文销毁。所有纹理重新上传 GPU,模型重新解析,场景从头构建——通常要 1-3 秒加载时间。

仪表盘组件

一个数据分析仪表盘有可拖拽的图表卡片。用户把一个图表从第 1 行拖到第 3 行。图表的当前滚动位置(大数据表)、tooltip 状态、时间轴缩放全部保留。

没有这个库:每次拖拽都销毁重建图表。数据从 API 重新拉取。大数据集产生明显的渲染卡顿。用户每次重排都看到 空白 → loading → 渲染完成 的闪烁。

注意

  • 依赖 React 内部的 __reactFiber$ 属性(非公开 API),内置运行时检测和警告。
  • 需要 React 16.8+(Hooks),已在 React 18 测试通过。
  • 迁移期间源容器会插入占位 <div>

许可证

MIT