React 指南

学习如何用 @reelkit/react.

触摸优先
带惯性和吸附的滑动
键盘导航
方向键 + Escape
滚轮滚动
可选,带防抖
虚拟化
10,000+ 条目,DOM 里只有 3 个
指示器
Instagram 风格的圆点滚动
编程式 API
通过 ref 调用 next()、prev()、goTo()
循环模式
无限循环导航
方向可选
竖向或横向
零重渲染
基于信号的状态更新

Reel 组件

Reel 组件是主容器,负责管理滑动器状态、处理触摸手势、键盘导航和动画。

DOM 里始终只有三张幻灯片:当前一张和它的前后两张,所以一万个条目的列表和十个条目的列表开销相同。想看它在完整功能里如何运作,比如无限信息流或打开播放器的网格,请查看实战示例。

tsx
import { Reel, ReelIndicator } from '@reelkit/react';

<Reel
  count={items.length}
  size={[width, height]}
  direction="vertical"
  enableWheel
  afterChange={(index) => console.log('Current:', index)}
  itemBuilder={(index, indexInRange, size) => (
    <div style={{ width: size[0], height: size[1] }}>
      Slide {index}
    </div>
  )}
>
  {/* Optional children like ReelIndicator */}
</Reel>

自动尺寸

size 属性是可选的。省略时,Reel 会通过 ResizeObserver 自动测量容器,并适配由 CSS 决定的布局。容器的尺寸必须由父级决定(例如 flex、grid 或显式的 CSS 尺寸)。

tsx
// Explicit size (fixed)
<Reel count={items.length} size={[400, 600]} itemBuilder={...} />

// Auto-size (responsive — sized by CSS)
<Reel count={items.length} style={{ width: '100%', height: '100dvh' }} itemBuilder={...} />

itemBuilder 模式

itemBuilder 属性是一个函数,接收索引并返回该幻灯片的内容。正是这个模式让虚拟化成为可能 —— 只有可见的条目会被渲染。

tsx
itemBuilder={(index, indexInRange, size) => {
  // index: actual item index (0 to count-1)
  // indexInRange: position in visible window (0, 1, or 2)
  // size: [width, height] of the container
  return <Slide index={index} />;
}}

内置的导航方式:

  • 触摸 / 滑动: 拖动即可翻页,带惯性和吸附
  • 键盘: 方向键和 Escape
  • 鼠标滚轮: 用 enableWheel 属性开启
  • 编程式: 使用 apiRef for next(), prev(), goTo()
tsx
import { useRef } from 'react';
import { Reel, type ReelApi } from '@reelkit/react';

function App() {
  const apiRef = useRef<ReelApi>(null);

  return (
    <>
      <Reel
        count={10}
        size={[400, 600]}
        apiRef={apiRef}
        itemBuilder={(index) => <Slide index={index} />}
      />
      <button onClick={() => apiRef.current?.prev()}>Prev</button>
      <button onClick={() => apiRef.current?.next()}>Next</button>
      <button onClick={() => apiRef.current?.goTo(5)}>Go to 5</button>
    </>
  );
}

URL 状态

useOverlayUrlState 为浮层构建一个 URL 状态控制器并整个返回,你再把它作为 *UrlOverlay 的 controller 属性传进去。打开状态归 URL 所有,因此绑定后的浮层会自己打开,链接就是通常的打开方式。参数原本不存在时第一次写入压入一条历史记录,之后每次写入都是替换,所以翻页永远不会把返回键埋掉。留着控制器就能读取 value/position ,也能编程式地驱动它: set(position) 打开, set(null) 关闭,而 set 正是浮层内部在切换幻灯片时使用的底层写入。

tsx
import { useOverlayUrlState, urlIndexKey } from '@reelkit/react';
import { useReactRouterUrlAdapter } from '@reelkit/react/react-router-url-adapter';
import { LightboxUrlOverlay } from '@reelkit/react-lightbox';
import { Link } from 'react-router-dom';

const photo = useOverlayUrlState({
  param: 'photo',
  ...urlIndexKey(() => images.length),
});

// Opening is a link — the overlay reads the URL and opens itself.
<Link to="?photo=3"><img src={images[3].src} /></Link>
<LightboxUrlOverlay controller={photo} images={images} />

// Read the url-derived state, or close programmatically (a low-level write).
photo.position.value; // 3 for ?photo=3, null when nothing is open
photo.set(null); // close

// Routed app: pass a router-backed adapter, otherwise the router's
// own location goes stale and its next navigation drops the param.
const adapter = useReactRouterUrlAdapter();
const routed = useOverlayUrlState({
  param: 'photo',
  adapter,
  ...urlIndexKey(() => images.length),
});

选项对象接受 param, codec、 locator (这三个都是必填的),外加可选的 adapter。 param 和 adapter 在首次渲染时读取,之后保持不变 —— 要更改请重新挂载;而 codec 和 locator 是实时读取的,因此列表增长或重新排序后的查找看到的是当前列表。 codec 和 locator 是共用同一个 Id的配套组合,因此总是一起出现 —— 对于普通的 ?photo=3 画廊,展开 ...urlIndexKey(() => images.length)即可,它会一次性返回两半。 urlIndexKey 把参数映射成幻灯片索引,并以 getter 返回的实时数量为上界,因此过期或越界的 ?photo=99 会被拒绝并自动从 URL 中消失,而不是打开一张 URL 从未指定的幻灯片。请传 getter 而不是数字,这样分页信息流增长时上界依然正确。它包装了 createIndexLocator (the locator half) and pairs it with indexCodec。分页信息流或按身份寻址的画廊则自行提供配套的 codec + locator 。完整的选项表见 React API 参考.

ReelIndicator

可选组件,显示 Instagram 风格的进度指示器,标出当前在滑动器中的位置。放在 Reel内部时,它会通过上下文自动连接到父级的 count 和 active 值 —— 不需要手动接状态。

tsx
import { Reel, ReelIndicator } from '@reelkit/react';

{/* Auto-connect: count and active are inherited from parent Reel */}
<Reel count={10} size={[400, 600]} itemBuilder={...}>
  <ReelIndicator />
</Reel>

{/* Manual usage: pass count and active explicitly (e.g. outside a Reel) */}
<ReelIndicator count={10} active={currentIndex} />

要点

  • size 属性

    可选的 [宽, 高] 元组;省略则由 CSS 自动决定尺寸

  • itemBuilder

    接收索引并返回幻灯片内容

  • apiRef

    访问控制器方法以进行导航

  • afterChange

    跟踪当前索引以更新界面

性能建议

  • 缓存数据数组

    把条目数组用 useMemo包起来。每次渲染都产生新的数组引用会触发一次 count 更新并重新计算可见范围。

  • 让 itemBuilder 保持轻量

    它在每次可见范围变化时都会运行(通常是 3 张幻灯片)。不要在里面做重计算或副作用。

  • 临近边缘时加载数据

    使用 afterChange 来检测用户是否接近末尾,并在幻灯片用完之前取下一批(见 无限信息流 示例)。

  • 在可滚动页面里关掉滚轮

    把 enableWheel={false} ,当滑动器嵌在可滚动布局里时,避免抢走页面滚动。

下一步