Expo UI 中的 Worklet 集成:同步控制 SwiftUI 和 Compose 状态

Expo UI 现已集成 react-native-worklets,让你能在 UI 线程上同步控制 SwiftUI 和 Compose 状态。无需 JS 往返通信。

中文
复制
Worklet integration in Expo UI: synchronously controlling SwiftUI and Compose state

在 SDK 56 中,Expo UI 首次与 UI runtime worklet 实现了原生集成,这要归功于 react-native-worklets。这意味着我们可以在 UI 线程上同步执行事件回调,并同步控制 UI 状态。也就是说,你可以这样写:

import { Host, TextInput, useNativeState } from '@expo/ui';

export default function Screen() {
  const value = useNativeState('');

  return (
    <Host matchContents>
      <TextInput
        value={value}
        placeholder="Type something"
        onChangeText={(value) => {
          'worklet';
          // Runs synchronously on the UI thread, on every keystroke.
          console.log('[UI thread] typed:', value);
        }}
      />
    </Host>
  );
}

注意:项目里需要安装 react-native-reanimatedreact-native-worklets 才能正常工作。

实际发生了什么

这里有两部分在协同工作:

useNativeState 会创建一个 ObservableState,即一个存在于原生侧的 SharedObject,由 SwiftUI 和 Compose 共同观察。底层在 iOS 上对应 ObservableObject,在 Android 上对应 MutableState

Worklet 回调(例如 onTextChange)会在原生视图触发事件时直接在 UI 线程上执行。

两者结合,TextField 中每敲一次键都会更新共享的 text 状态、运行你的 worklet,并让 SwiftUI 和 Compose 重新渲染,全程无需跳到 JS 线程。

如果你写过 SwiftUI,这套写法应该很眼熟。上面的 TS 代码几乎可以 1:1 对应到:

struct Screen: View {
  @State var text = ""

  var body: some View {
    TextField("Type something", text: $text)
      .onChange(of: text) { _, newValue in
        print("[UI thread] typed:", newValue)
      }
  }
}

useNativeState 扮演的是 @State 的角色,text={text} 相当于 TextField(text: $text),worklet onTextChange 则对应 .onChange(of:)。在 Compose 上用 mutableStateOfonValueChange 也是同样的结构。

无闪烁的同步输入掩码

这套机制最直接的收益是输入掩码终于能正常工作了。worklet 可以在按键到达的同一帧内改写 text.value,用户永远不会看到未掩码的字符,也不存在经过 JS 线程的异步往返。

下面这个信用卡输入框会在用户输入时于 UI 线程上把 4242424242424242 格式化为 4242 4242 4242 4242

import { Host, TextInput, useNativeState } from '@expo/ui/swift-ui';

export default function CardNumberField() {
  const value = useNativeState('');

  return (
    <Host matchContents>
      <TextInput
        value={value}
        placeholder="Card number"
        onChangeText={(value) => {
          'worklet';
          const digits = value.replace(/\\D/g, '').slice(0, 16);
          const masked = digits.replace(/(.{4})/g, '$1 ').trim();
          text.value = masked;
        }}
      />
    </Host>
  );
}

视频:blog_video_16x9 — 在 expo.dev 上观看

同样的模式也适用于电话号码、日期、邮政编码、货币,以及任何显示文本需要与原始按键不同的场景。

Worklet 集成为什么重要

Worklet 集成让 Expo UI 能提供更接近原生的体验,并在现有的异步方案之外给出一个同步选项,你可以根据交互需求来选择。

输入掩码只是其中一个用例。同样的「原生状态 + worklet」模式,能让 Expo UI 把更多基于原生状态的 SwiftUI 和 Compose API 带到 React Native 上。

我们很期待看到它接下来的发展。

试用

Worklet 在 @expo/ui/swift-ui@expo/ui/jetpack-compose 上均可使用,并且已经落地到 SDK 56TextInput 是首批接入的组件之一,后续版本中会有更多表单控件获得同步回调。

来源: Expo Blog← 返回首页