Reference version

This is documentation for the next SDK version. For up-to-date documentation, see the latest version (SDK 56).

useNativeState

一个 React Hook,用于创建在 JavaScript 和原生 Jetpack Compose 视图之间共享的可观察状态。

Android
Included in Expo Go

For the complete documentation index, see llms.txt. Use this file to discover all available pages.

useNativeState 返回一个 ObservableState,它在原生端映射到 Compose 的 MutableState,因此对 .value 的读取和写入会直接被 Compose 跟踪,而不会经过 React 的渲染周期。这使你可以在 UI 线程上的 worklet 中同步更新原生视图。

安装

Terminal
npx expo install @expo/ui

If you are installing this in an existing React Native app, make sure to install expo in your project.

用法

注意: 使用 worklet 需要在项目中安装 react-native-reanimatedreact-native-workletsuseNativeState 本身不依赖它们即可工作,但下面展示的 UI 线程同步更新依赖 worklet 运行时。

下面的示例在用户输入时对电话号码进行掩码处理。格式化以及对 maskedPhone.value(文本)和 selection.value(光标位置)的写入都在 UI 线程上同步发生,因此输入值和掩码值之间不会出现闪烁。

WorkletPhoneMaskExample.tsx
import { Host, TextField, Text as ComposeText, useNativeState } from '@expo/ui/jetpack-compose'; import { fillMaxWidth } from '@expo/ui/jetpack-compose/modifiers'; import { useCallback } from 'react'; export default function WorkletPhoneMaskExample() { const maskedPhone = useNativeState(''); const selection = useNativeState({ start: 0, end: 0 }); const handleValueChange = useCallback( (v: string) => { 'worklet'; const digits = v.replace(/\D/g, '').slice(0, 10); let formatted: string; if (digits.length === 0) { formatted = ''; } else if (digits.length <= 3) { formatted = digits; } else if (digits.length <= 6) { formatted = `(${digits.slice(0, 3)}) ${digits.slice(3)}`; } else { formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`; } if (formatted !== v) { maskedPhone.value = formatted; // 为演示而将光标跳到末尾。真正的掩码需要更智能的光标处理。 selection.value = { start: formatted.length, end: formatted.length }; } }, [maskedPhone, selection] ); return ( <Host matchContents> <TextField value={maskedPhone} selection={selection} keyboardOptions={{ keyboardType: 'phone' }} modifiers={[fillMaxWidth()]} onValueChange={handleValueChange}> <TextField.Placeholder> <ComposeText>(555) 123-4567</ComposeText> </TextField.Placeholder> </TextField> </Host> ); }

API

import { useNativeState } from '@expo/ui/jetpack-compose';

Hooks

useNativeState(initialValue)

Android
ParameterType
initialValueT

Creates an observable native state that is automatically cleaned up when the component unmounts. initialValue is captured once on the first render

Returns:
ObservableState<T>

Types

ObservableState

Android

Observable state shared between JavaScript and native views (Jetpack Compose on Android and SwiftUI on iOS).

Type: SharedObject extended by:

PropertyTypeDescription
onChange[listener] | null

A single listener invoked on the native UI runtime whenever the value changes (after iOS didSet and Android's setter). Assigning replaces the previous listener; assign null to clear. The initial value does not fire onChange.

The callback must be a worklet so it can run synchronously on the UI thread. Attach it inside useEffect and clear it in the cleanup so the listener lifecycle matches the component lifecycle.

Example

const state = useNativeState(0); useEffect(() => { state.onChange = (value) => { 'worklet'; console.log('changed to', value); }; }, []);
valueT

The current value.

Writes from a UI worklet are synchronous and immediately readable. Writes from the JS thread are scheduled to the UI thread asynchronously, the new value is not readable until the update has been applied. Prefer writing from a worklet when you need synchronous updates