How do I optimise the remotion player? #5329
Replies: 2 comments
|
Biggest wins, roughly in order of impact:
Profiling tip: React DevTools' profiler on the playing Player will show you exactly which component is re-rendering expensively per frame — it's usually one or two offenders, not everything. |
|
The "memoize everything" recommendation above is correct, but the question underneath it is more specific: when a Scene is not currently visible, does the Player still trigger a React re-render of it? Answer: the Player re-renders the whole component tree every tick, but A Scene that reads import { render, act } from "@testing-library/react";
import { createContext, useContext, useMemo, useState, memo } from "react";
const FrameContext = createContext<{ current: number } | null>(null);
function useCurrentFrame() {
const ctx = useContext(FrameContext);
if (!ctx) throw new Error("FrameContext missing");
return ctx.current;
}
const renderCounts = new Map<string, number>();
const bump = (label: string) =>
renderCounts.set(label, (renderCounts.get(label) ?? 0) + 1);
const Scene = memo(function Scene({ label }: { label: string }) {
bump(label);
const frame = useCurrentFrame();
return <div data-testid={`scene-${label}`}>{label}:{frame}</div>;
});
function StackMemoised({ scenes }: { scenes: string[] }) {
const list = useMemo(() => scenes, [scenes]);
return (
<>
{list.map((label) => (
<div key={label}><Scene label={label} /></div>
))}
</>
);
}
test("memoised parent: 5 forced re-renders do not cause Scenes to re-render", () => {
const StableParent = () => {
const [, force] = useState(0);
(StableParent as any)._advance = () => force((n) => n + 1);
const scenes = useMemo(() => ["A", "B", "C"], []);
return <StackMemoised scenes={scenes} />;
};
render(<FrameContext.Provider value={{ current: 0 }}><StableParent /></FrameContext.Provider>);
const before = Object.fromEntries(renderCounts);
for (let i = 0; i < 5; i++) act(() => { (StableParent as any)._advance(); });
const after = Object.fromEntries(renderCounts);
expect(after.A).toBe(before.A);
expect(after.B).toBe(before.B);
expect(after.C).toBe(before.C);
});That test passes. The next one is the failure mode people actually hit, where the Scene takes a non-primitive prop: const SceneWithPayload = memo(function SceneWithPayload({
label, payload
}: { label: string; payload: { tag: string } }) {
bump(label);
return <div data-testid={`scene-${label}`}>{label}:{payload.tag}</div>;
});
function StackWithInlinePayload({ scenes }: { scenes: string[] }) {
return (
<>
{scenes.map((label) => (
<SceneWithPayload label={label} payload={{ tag: label }} key={label} />
))}
</>
);
}Forced parent re-renders create a new So the practical checklist is:
I ran the two tests on If you post the offending component I can probably point at the prop that is missing a |
Uh oh!
There was an error while loading. Please reload this page.
Hi,
I am trying to optimise the remotion player, i have bunch of transitionSeries.Sequence and transitionSeries.Transition, in form of Sequence -> Transition -> Sequence -> Transition and so on.....the thing the components inside my Sequence are getting heavy, I have noticed all the sequence are rendered at once and they are behind each other altho only one is visible at a time.
When I play the player, the components using useCurrentFrame start re rendering, I thought they were bound to the nearest sequence and others should not start re rendering ? Is this obvious behavior or am I missing something?
All reactions