迁移指南
升级到 v0.5 beta(新原生运行时)
v0.5 在 Rive 新的 Android 和 iOS 原生运行时之上重建了 @rive-app/react-native。它目前是 beta 版本。
公共 React API 基本没有变化,但已弃用的同步 API 在新运行时上的行为有所不同。最安全的路径是先在最新的 v0.4 上——使用未弃用的异步 API——让应用正常运行,然后再切换到 beta。
推荐路径
- 升级到最新的 v0.4.x
npm install @rive-app/[email protected]需要
0.4.19或更高版本——更早的0.4.x版本不包含完整的异步 API 接口。这让你在迁移 API 使用的同时保持在稳定运行时上。
- 切换到未弃用的异步 API
在
v0.4上,将已弃用的同步 ViewModel 和属性访问器替换为异步等价物。将状态机输入、文本运行和事件转换为数据绑定。完整的替换列表请参见下方的迁移到异步 API。
💡异步 API 在
v0.4.19+ 和v0.5beta 上的工作方式相同。完成此迁移后,在两者之间切换只需更改依赖——你可以随时尝试 beta 并回滚到v0.4,无需修改代码。 - 尝试 v0.5 beta
安装 beta:
npm install @rive-app/react-native@beta请在 GitHub 上报告任何问题。
弃用时间线
已弃用的同步 API(以及状态机输入、文本运行和事件方法)正在随着运行时迁移到新原生基础而逐步淘汰:
| 版本 | 已弃用 API 的状态 |
|---|---|
v0.4 | 异步替代方案可用。已弃用 API 仍可正常使用,无警告。 |
v0.5 | 调用已弃用 API 会记录运行时警告。 |
v0.6 | 已弃用 API 被移除。 |
迁移到 v0.4.18+
此版本引入了异步视图模型实例创建,并弃用了同步创建路径。
useViewModelInstance 新增 async: true 与 isLoading
传入 async: true 以选择异步实例创建。该标志表示您的组件会处理加载状态:结果现在包含 isLoading,且实例在首次渲染时不可用——请在渲染时对其进行 gate。这为新的运行时实现做好了准备——在该实现中,创建实例是一个异步操作。
async: true 是一个过渡性标志;同步创建路径正在分阶段逐步淘汰:
- 现在 — 不带
async: true的调用在 TypeScript 中已弃用(在编辑器中以及通过 lint 规则产生弃用警告)。 - 下一步 — 不带
async: true的调用还将记录运行时弃用警告。 - 稍后 — 同步路径被移除,
async: true变为必需。 - 最终 — 标志本身被移除;hook 直接变为异步。
新版 API
const { riveFile, error: fileError } = useRiveFile(require('./animation.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
if (fileError || error) return <Text>{(fileError ?? error)!.message}</Text>;
if (isLoading || !instance) return <ActivityIndicator />;
return <RiveView file={riveFile} dataBind={instance} />;
旧版 API(已弃用)
const { riveFile } = useRiveFile(require('./animation.riv'));
const { instance, error } = useViewModelInstance(riveFile);
if (error) return <Text>{error.message}</Text>;
if (!instance) return <ActivityIndicator />;
return <RiveView file={riveFile} dataBind={instance} />;
async 必须在组件的整个生命周期内保持恒定——如需切换模式,请重新挂载(例如更改 key)。
useRive 的 Ref 起始为 undefined
useRive().riveViewRef 起始为 undefined(视图待定),而不是 null(失败/脱落),与 useRiveFile 的约定一致。使用 riveViewRef === null 作为"尚未就绪"检查的代码应改用 riveViewRef == null 或可选链。
其他变化
- 在同步(已弃用)路径上,为
null的源现在会结算为终态{ instance: null, isLoading: false },而不是无限期地报告加载状态。 - 在 Android 上,挂载后立即从视图 ref 读取自动绑定实例,在绑定完成期间可能会短暂解析为
null。async: true路径会等待实例;应避免通过已弃用的同步路径进行一次性读取。
快速参考
| 之前 | 替换 |
|---|---|
const { instance, error } = useViewModelInstance(file) | const { instance, isLoading, error } = useViewModelInstance(file, { async: true }) |
riveViewRef === null(未就绪检查) | riveViewRef == null |
迁移到 v0.4.0+
此版本改进了 hooks 的错误透明度和加载语义,为异步实验性运行时做准备。
useViewModelInstance 返回 { instance, error }
该 hook 现在返回一个可区分联合类型,而不是 ViewModelInstance | null:
{ instance: undefined, error: null }— 加载中(源未就绪){ instance: ViewModelInstance, error: null }— 成功{ instance: null, error: null }— 已解析,未找到 ViewModel{ instance: null, error: Error }— 查找失败
新版 API
const { riveFile } = useRiveFile(require('./animation.riv'));
const { instance, error } = useViewModelInstance(riveFile);
if (error) return <Text>{error.message}</Text>;
if (!instance) return <ActivityIndicator />;
return <RiveView file={riveFile} dataBind={instance} />;
旧版 API
const { riveFile } = useRiveFile(require('./animation.riv'));
const instance = useViewModelInstance(riveFile);
if (!instance) return <ActivityIndicator />;
return <RiveView file={riveFile} dataBind={instance} />;
useRiveFile Error 现在是 Error
error 字段现在是一个 Error 对象而不是 string,并且 riveFile 在加载期间为 undefined(之前是 null)。isLoading 保留以便使用。
新版 API
const { riveFile, isLoading, error } = useRiveFile(require('./animation.riv'));
if (error) return <Text>{error.message}</Text>;
旧版 API
const { riveFile, isLoading, error } = useRiveFile(require('./animation.riv'));
if (error) return <Text>{error}</Text>;
属性 Hooks 从 undefined 开始
useRiveNumber、useRiveString、useRiveBoolean、useRiveColor 和 useRiveEnum 在挂载时不再同步读取 property.value。值初始为 undefined,并通过属性监听器到达。
const { value: health } = useRiveNumber('health', instance);
// 第一次渲染时 health 为 undefined — 使用前需守卫
<Text>{health !== undefined ? health.toFixed(2) : '...'}</Text>
// 在更新器函数中守卫
setHealth((prev) => (prev ?? 0) + 1);
快速参考
| 之前 | 替换 |
|---|---|
const instance = useViewModelInstance(file) | const { instance, error } = useViewModelInstance(file) |
{error}(在 JSX 中,来自 useRiveFile) | {error?.message} |
riveFile === null(加载检查) | riveFile === undefined 或使用 isLoading |
health 在首次渲染时同步可用 | 为 undefined 做守卫:health !== undefined ? ... : ... |
迁移到 Async API(v0.3.2+)
此版本引入了异步优先 API,为新的实验性 Rive 运行时做准备。阻塞 JS 线程的同步方法已被弃用,替换为异步等效方法。状态机输入和文本运行方法已被弃用,推荐使用数据绑定,并将在实验性运行时中完全移除(因此也将在即将推出的 @rive-app/react-native 版本中移除)。
变化内容
- 异步方法替换了所有同步的 ViewModel 和属性访问器
- 基于名称的访问替换了基于计数/索引的 ViewModel 和画板查找
getValueAsync()/set()替换了property.value用于读取和写入属性- 状态机输入、文本运行和事件已被弃用,将在实验性运行时中移除 — 请改用数据绑定
迁移步骤
1. ViewModel 访问
新版 API:
const names = await file.getViewModelNamesAsync();
const vm = await file.viewModelByNameAsync('Person');
const defaultVM = await file.defaultArtboardViewModelAsync();
已弃用 API:
const count = file.viewModelCount;
const vm = file.viewModelByName('Person');
const defaultVM = file.defaultArtboardViewModel();
2. 实例创建
新版 API:
const instance = await vm.createDefaultInstanceAsync();
const named = await vm.createInstanceByNameAsync('player1');
const blank = await vm.createBlankInstanceAsync();
已弃用 API:
const instance = vm.createDefaultInstance();
const named = vm.createInstanceByName('player1');
const blank = vm.createInstance();
useViewModelInstancehook 为您处理 ViewModel 解析和实例创建 — 在大多数情况下,您不需要直接调用这些方法。
3. 属性值访问
新版 API:
const num = await prop.getValueAsync();
prop.set(42);
已弃用 API:
const num = prop.value;
prop.value = 42;
4. 嵌套 ViewModelInstance 访问
新版 API:
const nested = await instance.viewModelAsync('Header');
已弃用 API:
const nested = instance.viewModel('Header');
5. 列表属性访问
新版 API:
const len = await listProp.getLengthAsync();
const item = await listProp.getInstanceAtAsync(0);
已弃用 API:
const len = listProp.length;
const item = listProp.getInstanceAt(0);
6. 画板访问
新版 API:
const count = await file.getArtboardCountAsync();
const names = await file.getArtboardNamesAsync();
已弃用 API:
const count = file.artboardCount;
const names = file.artboardNames;
7. 异步设置模式
用于 ViewModel 设置的同步 useMemo 链应该替换为 useState + useEffect,或者用 useViewModelInstance hook 简化。
useViewModelInstance(推荐)
const { riveFile } = useRiveFile(require('./animation.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const { value: health, setValue: setHealth } = useRiveNumber(
'health',
instance
);
if (!instance) return <ActivityIndicator />;
return <RiveView file={riveFile} dataBind={instance} />;
手动异步设置
const { riveFile } = useRiveFile(require('./animation.riv'));
const [instance, setInstance] = useState<ViewModelInstance>();
useEffect(() => {
if (!riveFile) return;
let cancelled = false;
(async () => {
const vm = await riveFile.defaultArtboardViewModelAsync();
const vmi = await vm?.createDefaultInstanceAsync();
if (!cancelled) setInstance(vmi ?? undefined);
})();
return () => { cancelled = true; };
}, [riveFile]);
已弃用的同步模式
function Setup({ file }) {
const vm = useMemo(() => file.defaultArtboardViewModel(), [file]);
const instance = useMemo(
() => vm?.createDefaultInstance(),
[vm]
);
if (!instance) return null;
return <MyComponent instance={instance} file={file} />;
}
快速参考
| 已弃用 | 替换 |
|---|---|
file.viewModelByName(name) | await file.viewModelByNameAsync(name) |
file.defaultArtboardViewModel() | await file.defaultArtboardViewModelAsync() |
file.viewModelCount | (await file.getViewModelNamesAsync()).length |
file.viewModelByIndex(i) | await file.viewModelByNameAsync(name) |
file.artboardCount / artboardNames | await file.getArtboardCountAsync() / getArtboardNamesAsync() |
vm.createDefaultInstance() | await vm.createDefaultInstanceAsync() |
vm.createInstanceByName(name) | await vm.createInstanceByNameAsync(name) |
vm.createInstance() | await vm.createBlankInstanceAsync() |
instance.viewModel(path) | await instance.viewModelAsync(path) |
listProp.length | await listProp.getLengthAsync() |
listProp.getInstanceAt(i) | await listProp.getInstanceAtAsync(i) |
listProp.addInstance(inst) | await listProp.addInstanceAsync(inst) |
listProp.addInstanceAt(inst, i) | await listProp.addInstanceAtAsync(inst, i) |
listProp.removeInstance(inst) | await listProp.removeInstanceAsync(inst) |
listProp.removeInstanceAt(i) | await listProp.removeInstanceAtAsync(i) |
listProp.swap(i, j) | await listProp.swapAsync(i, j) |
prop.value(读取) | await prop.getValueAsync() |
prop.value = x(写入) | prop.set(x) |
平台注意事项
| 限制 | 详情 |
|---|---|
replaceViewModel() | Android 上空操作。iOS 上有效。 |
图像/列表属性上的 addListener() | 两个平台上均为空操作。改用 getLengthAsync() 轮询。 |
addInstanceAt() / swap() 返回值 | iOS 上始终为 true。Android 返回正确值。 |
instanceName | 除通过 createInstanceByNameAsync() 创建的实例外,为空字符串。 |
Android 上的 defaultArtboardViewModel() | 使用启发式方法。建议使用带有显式名称的 viewModelByNameAsync(name)。 |
从 v0.1.0 迁移到 v0.2.0
此更改更新到 Nitro 的新主要版本以解决视图回收问题。详情请参见发布说明。
唯一需要的更改是更新应用中使用的 Nitro 版本。
从 rive-react-native 迁移到 @rive-app/react-native
新版 Rive React Native 运行时(@rive-app/react-native)是使用 Nitro Modules 构建的完全重写版本,提供更好的性能和更好的 React Native 集成。
您的所有 Rive 图形看起来和功能仍将与之前一样。
新特性
RiveFile 所有权: 您现在通过 useRiveFile hook 拥有 RiveFile 对象,支持文件缓存、从一个文件创建多个实例以及更好的资源管理。
// 旧版: 文件内部管理
<Rive url="https://cdn.rive.app/animations/vehicles.riv" />
// 新版: 您拥有 RiveFile
const { riveFile } = useRiveFile(require("./vehicles.riv"));
<RiveView file={riveFile} />
增强的数据绑定: 直接访问 ViewModel 和 ViewModelInstance 对象,支持初始化 hooks、多个实例以及所有属性类型(列表、图像、画板)。
// 旧版: hooks 接受 riveRef,返回元组
const [setRiveRef, riveRef] = useRive();
const [health, setHealth] = useRiveNumber(riveRef, "health");
// 新版: hooks 接受 viewModelInstance,返回对象
const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const { value: health, setValue: setHealth } = useRiveNumber(
"health",
viewModelInstance
);
改进的错误处理: 错误处理得到改进。参见错误处理文档了解更多信息。
系统要求
- React Native:0.78+(推荐 0.79+)
- Expo SDK:53+(Expo 用户)
- iOS:15.1+
- Android:SDK 24+
- Xcode:16.4+
- JDK:17+
- Nitro Modules:0.25.2+
迁移步骤
1. 安装
npm uninstall rive-react-native
npm install @rive-app/react-native react-native-nitro-modules
react-native-nitro-modules是必需的,因为此库依赖于 Nitro Modules。
2. 更新导入
// 旧版
import Rive from "rive-react-native";
// 新版
import { RiveView } from "@rive-app/react-native";
3. 加载 Rive 文件
新版运行时:
const { riveFile, isLoading, error } = useRiveFile(require('./animation.riv'));
// 也支持: URL 字符串、资源名称或 ArrayBuffer
return <RiveView file={riveFile} style={{ width: 400, height: 400 }} />;
旧版运行时:
<Rive source={require('./animation.riv')} />
// 也支持: url 或 resourceName 属性
参见加载 Rive 文件了解更多信息。
4. 组件迁移
新版运行时:
<RiveView
file={riveFile}
artboardName="MainArtboard"
stateMachineName="State Machine 1"
fit={Fit.Contain}
autoPlay={true}
style={{ width: 400, height: 400 }}
/>
旧版运行时:
<Rive
url="https://cdn.rive.app/animations/vehicles.riv"
artboardName="MainArtboard"
stateMachineName="State Machine 1"
fit={Fit.Contain}
autoplay={true}
style={{ width: 400, height: 400 }}
/>
参见 React Native 运行时和属性文档了解更多信息。
5. 视图引用迁移
新版运行时:
const { riveViewRef, setHybridRef } = useRive();
riveViewRef?.play();
riveViewRef?.pause();
<RiveView hybridRef={setHybridRef} file={riveFile} />
旧版运行时:
const [setRiveRef, riveRef] = useRive();
riveRef?.play();
riveRef?.pause();
<Rive ref={setRiveRef} url="..." />
参见 Rive ref 方法了解更多信息。
6. 状态机输入(已弃用)
⚠️ 这些方法已弃用。请迁移到数据绑定。
新版运行时:
riveViewRef?.setNumberInputValue('level', 5);
riveViewRef?.setBooleanInputValue('isActive', true);
riveViewRef?.triggerInput('buttonPressed');
旧版运行时:
riveRef.current?.setInputState('State Machine 1', 'level', 5);
riveRef.current?.setInputState('State Machine 1', 'isActive', true);
riveRef.current?.fireState('State Machine 1', 'buttonPressed');
参见状态机输入了解更多信息。
7. Rive 事件(已弃用)
⚠️ 这些方法已弃用。请改用数据绑定触发器。
新版运行时:
useEffect(() => {
const handleEvent = (event: RiveEvent) => console.log(event);
riveViewRef?.onEventListener(handleEvent);
return () => riveViewRef?.removeEventListeners();
}, [riveViewRef]);
旧版运行时:
<Rive
onRiveEventReceived={(event) => console.log(event)}
url="..."
/>
参见运行时事件了解更多信息。
8. 数据绑定
新版运行时通过让您直接访问 ViewModelInstance 对象显著改进了数据绑定。这支持:
- 初始化 hooks — 在渲染前使用
onInit回调设置初始属性值 - 多个实例 — 从同一个文件创建和管理多个视图模型实例
- 高级属性类型 — 完全支持列表、图像、画板和嵌套视图模型
- 更好的 React 集成 — 属性 hooks 与 React 状态和生命周期无缝集成
主要 API 变化:
- 属性 hooks 接受
viewModelInstance而不是riveRef - Hooks 返回对象而不是元组
- 参数交换:
(path, viewModelInstance)与(riveRef, path)
新版运行时:
const { riveFile } = useRiveFile(require('./animation.riv'));
const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const { value: health, setValue: setHealth } = useRiveNumber('health', viewModelInstance);
const { value: name, setName } = useRiveString('Player/Name', viewModelInstance);
const { trigger } = useRiveTrigger('gameOver', viewModelInstance, {
onTrigger: () => console.log('Game Over!')
});
<RiveView file={riveFile} dataBind={viewModelInstance} />
旧版运行时:
const [setRiveRef, riveRef] = useRive();
const [health, setHealth] = useRiveNumber(riveRef, 'health');
const [name, setName] = useRiveString(riveRef, 'Player/Name');
useRiveTrigger(riveRef, 'gameOver', () => console.log('Game Over!'));
<Rive ref={setRiveRef} resourceName="animation" dataBinding={AutoBind(true)} />
参见 React Native 数据绑定了解更多信息。
9. 带外资源
新版运行时:
const { riveFile } = useRiveFile(
require('./animation.riv'),
{
referencedAssets: {
'Inter-594377': {
source: require('./fonts/Inter-594377.ttf'),
},
'my-image': {
source: { uri: 'https://example.com/image.png' },
},
},
}
);
旧版运行时:
<Rive
url="..."
referencedAssets={{
'Inter-594377': {
source: require('./fonts/Inter-594377.ttf'),
},
}}
/>
参见加载资源了解更多信息。
10. 文本运行更新(已弃用)
⚠️ 直接文本运行方法(
.setTextRunValue()、.getTextRunValue())已弃用。请迁移到数据绑定字符串。
数据绑定(推荐):
const { value: playerName, setValue: setPlayerName } = useRiveString('playerName', viewModelInstance);
setPlayerName('John Doe');
新版运行时(已弃用):
// 新版运行时仍像旧版运行时一样支持文本运行方法
riveViewRef?.setTextRunValue('playerName', 'John Doe');
const name = riveViewRef?.getTextRunValue('playerName');
旧版运行时:
riveRef.current?.setTextRunValue('playerName', 'John Doe');
参见文本运行了解更多信息。
11. 回调
新版运行时:
<RiveView
file={riveFile}
onError={(error) => console.error('Rive error:', error)}
/>
// 对于状态变化,使用数据绑定监听器。例如,触发器:
const { trigger } = useRiveTrigger('onStateChange', viewModelInstance, {
onTrigger: () => console.log('State changed')
});
旧版运行时:
<Rive
onPlay={(name, isSM) => console.log('Playing:', name)}
onPause={(name, isSM) => console.log('Paused:', name)}
onStateChanged={(sm, state) => console.log('State changed:', state)}
onError={(error) => console.error('Error:', error)}
/>
参见数据绑定了解更多信息。
获取帮助
如果遇到问题: