编辑器入门中的状态管理
Editor Starter 中的状态通过内置的 React 状态管理工具管理:useState() 和 useContext()。
🌐 State in the Editor Starter is managed with the built-in React state management utilities: useState() and useContext().
为什么?
编辑器启动器的构建方式是为了让尽可能多的不同团队能够轻松采用它。
我们选择使用 React 的原生状态管理,以保持入门套件的轻量化,最小化外部依赖,并利用已经熟悉 React 的开发者熟悉的模式。
虽然默认的React状态管理工具对一些人来说具有争议,但任何React应用都可以使用它们,并且开发者对它们最为熟悉。
形状
🌐 Shape
Editor Starter 的整个状态存储在一个单一对象中,其形状由 EditorState 定义。
🌐 The whole state of the Editor Starter is stored in a single object with the shape defined by the EditorState.
src/editor/state/types.tstype DeletedAsset = { remoteUrl: string | null; remoteFileKey: string | null; assetId: string; statusAtDeletion: AssetState; }; export type UndoableState = { tracks: TrackType[]; assets: Record<string, EditorStarterAsset>; items: Record<string, EditorStarterItem>; fps: number; compositionWidth: number; compositionHeight: number; deletedAssets: DeletedAsset[]; }; export type EditorState = { undoableState: UndoableState; selectedItems: string[]; textItemEditing: string | null; textItemHoverPreview: TextItemHoverPreview | null; itemSelectedForCrop: string | null; renderingTasks: RenderingTask[]; captioningTasks: CaptioningTask[]; initialized: boolean; itemsBeingTrimmed: ItemBeingTrimmed[]; loop: boolean; assetStatus: Record<string, AssetState>; };
undoableState- 受撤销栈影响的状态:tracks:一组时间轴轨道,最后的那些是在后面渲染的。assets:已上传到编辑器的所有资源的地图。items:编辑器中已添加的所有 项目 的地图。fps:帧率(保存在状态中,但默认情况下没有界面可以更改它)。compositionWidth:画布的宽度。compositionHeight:画布的高度。deletedAssets:一组已被删除的资源 that have been deleted。
selectedItems:当前选中的项目ID数组。textItemEditing:当前正在编辑的文本项的 ID(如果有的话)。textItemHoverPreview:预览文本项的更新(例如,如果在字体选择器中悬停某个字体,文本将临时以悬停的字体显示)。itemSelectedForCrop:当前处于裁剪模式的项目的ID,如果有的话。renderingTasks:渲染过程的状态。captioningTasks:字幕处理的状态。initialized:编辑器是否已初始化,如果未初始化,画布将不可见。itemsBeingTrimmed:当前正在被修剪的项目数组,用于显示可能的最大修剪量。loop:是否应循环播放。assetStatus:资源ID到其上传状态的映射,可能为:pending-upload:该资源正在上传中。uploaded:资源已成功上传。error:资源上传失败。in-progress:该资源尚未上传。
无法撤销的状态
🌐 Undoable state
状态被分为可撤销和不可撤销的部分。
可撤销状态位于根状态的 undoableState 对象中。
🌐 State is separated into undoable and non-undoable parts.
Undoable state is located within the undoableState object of the root state.
无法撤销的状态 可能是:
- 项目的位置、大小及其他属性
- 资源和轨道
- 视频属性,如尺寸和帧率
不可撤销状态 可能是:
- 资源上传进度和状态
- 字幕进度
- 缩放级别
- 渲染状态
- 选择状态
另请参见:撤销和重做
🌐 See also: Undo and Redo
上下文
🌐 Contexts
在 src/editor-context-provider.tsx 中,你会看到一个非常深层嵌套的各种上下文提供者的树。
🌐 In src/editor-context-provider.tsx, you will see a very deeply nested tree of various context providers.
这是故意的,并且实现了这样的效果:当更新状态的一部分时,只有依赖于该部分状态的组件会重新渲染,而其他组件不会。
🌐 This is intentional and achieves that when updating a portion of the state, only the components that are dependent on that portion of the state will re-render, while the rest of the components will not.
为了获得最佳性能,我们建议你在自己的应用中继续使用此模式。
🌐 For the best performance, we recommend that you continue to use this pattern in your own application.
当没有变化时防止状态更新
🌐 Preventing state updates when nothing has changed
当没有任何变化时,你应该防止不必要的状态更新。
🌐 You should prevent an unnecessary state update when nothing has changed.
- 这对于性能更好
- 这将防止将快照添加到撤销堆栈中,否则单击撤销按钮一次将无效
在整个代码库中,你会看到一些检查,用于防止不必要的状态更新。
🌐 Throughout the codebase, you will see checks that prevent an unnecessary state update.
Preventing an identity changeexport const markAsDragging = (state: EditorState, itemId: string): EditorState => { return changeItem(state, itemId, (item) => { if (item.isDragging) { // The item would not change, so we return the original object return item; } return { ...item, isDragging: true, }; }); };
以命令式读取状态
🌐 Reading the state imperatively
如果你只需要在交互时访问状态,你可以使用 useCurrentStateAsRef() 钩子。它允许你在需要时以命令式方式访问状态。
🌐 If you only need to access the state upon an interaction, you can use the useCurrentStateAsRef() hook.
It allows you to imperatively access the state when you need it.
你无法用它构建响应式用户界面,但它比使用在状态改变时重新渲染的钩子性能更高。
🌐 You cannot build a reactive UI with it, but is more performant than using a hook that re-renders when the state changes.
例如:一个仅在用户点击时才需要访问状态的保存按钮。
🌐 For example: A save button that only needs to access the state when the user clicks it.
另请参阅
🌐 See also