数据获取
在渲染之前获取数据v4.0.0
您可以使用<Composition />
组件的calculateMetadata
属性来修改传递给您的React组件的props。
何时使用
使用calculateMetadata()
获取的数据必须是JSON可序列化的。这意味着它适用于API响应,但不适用于二进制格式的资产。
用法
传递一个回调函数,该函数接受未转换的props
,并返回一个具有新props的对象。
src/Root.tsxtsx
import {Composition } from "remotion";typeApiResponse = {title : string;description : string;};typeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = () => null;export constRoot :React .FC = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}calculateMetadata ={async ({props }) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};}}/>);};
src/Root.tsxtsx
import {Composition } from "remotion";typeApiResponse = {title : string;description : string;};typeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = () => null;export constRoot :React .FC = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}calculateMetadata ={async ({props }) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};}}/>);};
传递给calculateMetadata()
的props
是输入props与默认props合并在一起的结果。
除了props
之外,defaultProps
也可以从同一对象中读取。
在转换时,输入和输出必须是相同的TypeScript类型。
考虑为您的数据使用可空类型,并在组件内部抛出错误以处理null
类型:
MyComp.tsxtsx
typeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
MyComp.tsxtsx
typeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
TypeScript类型
您可以使用remotion
中的CalculateMetadataFunction
类型来为您的回调函数指定类型。将您的props的类型作为泛型值(<>
)传递。
src/Root.tsxtsx
import {CalculateMetadataFunction } from "remotion";export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {return {props : {...props ,data : {title : "Hello world",description : "This is a description",},},};};export constMyComp :React .FC <MyCompProps > = () => null;
src/Root.tsxtsx
import {CalculateMetadataFunction } from "remotion";export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {return {props : {...props ,data : {title : "Hello world",description : "This is a description",},},};};export constMyComp :React .FC <MyCompProps > = () => null;
协同定位
以下是如何在同一文件中定义模式、组件和获取器函数的示例:
MyComp.tsxtsx
import {CalculateMetadataFunction } from "remotion";import {z } from "zod";constapiResponse =z .object ({title :z .string (),description :z .string () });export constmyCompSchema =z .object ({id :z .string (),data :z .nullable (apiResponse ),});typeProps =z .infer <typeofmyCompSchema >;export constcalcMyCompMetadata :CalculateMetadataFunction <Props > = async ({props ,}) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <Props > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
MyComp.tsxtsx
import {CalculateMetadataFunction } from "remotion";import {z } from "zod";constapiResponse =z .object ({title :z .string (),description :z .string () });export constmyCompSchema =z .object ({id :z .string (),data :z .nullable (apiResponse ),});typeProps =z .infer <typeofmyCompSchema >;export constcalcMyCompMetadata :CalculateMetadataFunction <Props > = async ({props ,}) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <Props > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
src/Root.tsxtsx
importReact from "react";import {Composition } from "remotion";import {MyComp ,calcMyCompMetadata ,myCompSchema } from "./MyComp";export constRoot = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}schema ={myCompSchema }calculateMetadata ={calcMyCompMetadata }/>);};
src/Root.tsxtsx
importReact from "react";import {Composition } from "remotion";import {MyComp ,calcMyCompMetadata ,myCompSchema } from "./MyComp";export constRoot = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}schema ={myCompSchema }calculateMetadata ={calcMyCompMetadata }/>);};
通过实现这种模式,props编辑器中的id
现在可以进行调整,并且当数据更改时,Remotion将重新获取数据。
根据数据设置持续时间
您可以通过在回调函数中返回这些键来设置durationInFrames
、fps
、width
和height
:
tsx
import {CalculateMetadataFunction } from "remotion";typeMyCompProps = {durationInSeconds : number;};export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {constfps = 30;constdurationInSeconds =props .durationInSeconds ;return {durationInFrames :durationInSeconds *fps ,fps ,};};
tsx
import {CalculateMetadataFunction } from "remotion";typeMyCompProps = {durationInSeconds : number;};export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {constfps = 30;constdurationInSeconds =props .durationInSeconds ;return {durationInFrames :durationInSeconds *fps ,fps ,};};
在变量元数据页面了解更多关于此功能的信息。
中止过时请求
在属性编辑器中,属性可能会快速变化,例如快速输入。
最佳实践是使用传递给calculateMetadata()
函数的abortSignal
来取消过时的请求:
src/MyComp.tsxtsx
export const calculateMyCompMetadata: CalculateMetadataFunction<MyCompProps> = async ({ props, abortSignal }) => {const data = await fetch(`https://example.com/api/${props.id}`, { signal: abortSignal, });const json = await data.json();return { props: {...props,data: json,},};};export const MyComp: React.FC<MyCompProps> = () => null;
src/MyComp.tsxtsx
export const calculateMyCompMetadata: CalculateMetadataFunction<MyCompProps> = async ({ props, abortSignal }) => {const data = await fetch(`https://example.com/api/${props.id}`, { signal: abortSignal, });const json = await data.json();return { props: {...props,data: json,},};};export const MyComp: React.FC<MyCompProps> = () => null;
这个abortSignal
是由 Remotion 使用AbortController
API 创建的。
防抖请求
如果您正在向一个昂贵的 API 发送请求,您可能希望只在用户停止输入一段时间后才发出请求。您可以使用以下函数来实现:
src/wait-for-no-input.tstsx
import {getRemotionEnvironment } from "remotion";export constwaitForNoInput = (signal :AbortSignal ,ms : number) => {// Don't wait during renderingif (getRemotionEnvironment ().isRendering ) {returnPromise .resolve ();}if (signal .aborted ) {returnPromise .reject (newError ("stale"));}returnPromise .race <void>([newPromise <void>((_ ,reject ) => {signal .addEventListener ("abort", () => {reject (newError ("stale"));});}),newPromise <void>((resolve ) => {setTimeout (() => {resolve ();},ms );}),]);};
src/wait-for-no-input.tstsx
import {getRemotionEnvironment } from "remotion";export constwaitForNoInput = (signal :AbortSignal ,ms : number) => {// Don't wait during renderingif (getRemotionEnvironment ().isRendering ) {returnPromise .resolve ();}if (signal .aborted ) {returnPromise .reject (newError ("stale"));}returnPromise .race <void>([newPromise <void>((_ ,reject ) => {signal .addEventListener ("abort", () => {reject (newError ("stale"));});}),newPromise <void>((resolve ) => {setTimeout (() => {resolve ();},ms );}),]);};
src/MyComp.tsxtsx
export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = async ({props ,abortSignal }) => {awaitwaitForNoInput (abortSignal , 750);constdata = awaitfetch (`https://example.com/api/${props .id }`, {signal :abortSignal ,});constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <MyCompProps > = () => null;
src/MyComp.tsxtsx
export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = async ({props ,abortSignal }) => {awaitwaitForNoInput (abortSignal , 750);constdata = awaitfetch (`https://example.com/api/${props .id }`, {signal :abortSignal ,});constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <MyCompProps > = () => null;
时间限制
当 Remotion 调用calculateMetadata()
函数时,它会将其包装在一个默认超时时间为 30 秒的delayRender()
中。
在渲染过程中获取数据
使用delayRender()
和continueRender()
,您可以告诉 Remotion 在渲染帧之前等待异步操作完成。
何时使用
使用这种方法来加载不是 JSON 可序列化的资源,或者如果您使用的 Remotion 版本低于 4.0。
用法
尽早调用delayRender()
,例如在组件内部初始化状态时。
tsx
import {useCallback ,useEffect ,useState } from "react";import {cancelRender ,continueRender ,delayRender } from "remotion";export constMyComp = () => {const [data ,setData ] =useState (null);const [handle ] =useState (() =>delayRender ());constfetchData =useCallback (async () => {try {constresponse = awaitfetch ("http://example.com/api");constjson = awaitresponse .json ();setData (json );continueRender (handle );} catch (err ) {cancelRender (err );}}, [handle ]);useEffect (() => {fetchData ();}, [fetchData ]);return (<div >{data ? (<div >This video has data from an API! {JSON .stringify (data )}</div >) : null}</div >);};
tsx
import {useCallback ,useEffect ,useState } from "react";import {cancelRender ,continueRender ,delayRender } from "remotion";export constMyComp = () => {const [data ,setData ] =useState (null);const [handle ] =useState (() =>delayRender ());constfetchData =useCallback (async () => {try {constresponse = awaitfetch ("http://example.com/api");constjson = awaitresponse .json ();setData (json );continueRender (handle );} catch (err ) {cancelRender (err );}}, [handle ]);useEffect (() => {fetchData ();}, [fetchData ]);return (<div >{data ? (<div >This video has data from an API! {JSON .stringify (data )}</div >) : null}</div >);};
一旦数据被获取,您可以调用continueRender()
告诉 Remotion 继续渲染视频。
如果数据获取失败,您可以调用cancelRender()
取消渲染而不必等待超时。
时间限制
您需要在页面打开后的 30 秒内清除由delayRender()
创建的所有句柄。您可以增加超时时间。```
防止过度获取
在渲染过程中,会打开多个无头浏览器选项卡以加快渲染速度。
在 Remotion Lambda 中,渲染并发性 可以高达 200 倍。
这意味着如果您在组件内部获取数据,数据获取将会执行多次。
frame
不是 useEffect()
的依赖项,直接或间接地,否则数据将在每帧中获取,导致减速并有可能遇到速率限制。