refactor: prevent unnecessary prop drilling

Components throughout the tree are anyways utilizing the zustand store,
so there is no reason to send props for the same store methods.
This commit is contained in:
talksik
2026-03-12 10:07:56 -07:00
parent 0265dbdac4
commit 5ce61d06b2
3 changed files with 22 additions and 17 deletions
@@ -8,7 +8,6 @@ import { useAudioSource } from "@/components/audio/use-audio-source";
interface MediaParticleViewProps { interface MediaParticleViewProps {
particle: StreamParticle; particle: StreamParticle;
onEnded: () => void;
} }
function formatTime(ms: number): string { function formatTime(ms: number): string {
@@ -36,12 +35,12 @@ function DurationPill({
export function MediaParticleView({ export function MediaParticleView({
particle, particle,
onEnded,
}: MediaParticleViewProps) { }: MediaParticleViewProps) {
const cachedUrl = usePlaybackStore( const cachedUrl = usePlaybackStore(
(s) => s.downloadUrlCache[particle.id], (s) => s.downloadUrlCache[particle.id],
); );
const cacheDownloadUrl = usePlaybackStore((s) => s.cacheDownloadUrl); const cacheDownloadUrl = usePlaybackStore((s) => s.cacheDownloadUrl);
const next = usePlaybackStore((s) => s.next);
const paused = usePlaybackStore((s) => s.paused); const paused = usePlaybackStore((s) => s.paused);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@@ -113,7 +112,7 @@ export function MediaParticleView({
crossOrigin="anonymous" crossOrigin="anonymous"
src={cachedUrl} src={cachedUrl}
autoPlay autoPlay
onEnded={onEnded} onEnded={next}
onTimeUpdate={(e) => { onTimeUpdate={(e) => {
setCurrentTimeMs(e.currentTarget.currentTime * 1000); setCurrentTimeMs(e.currentTarget.currentTime * 1000);
}} }}
@@ -134,7 +133,7 @@ export function MediaParticleView({
src={cachedUrl} src={cachedUrl}
autoPlay autoPlay
playsInline playsInline
onEnded={onEnded} onEnded={next}
onTimeUpdate={(e) => { onTimeUpdate={(e) => {
setCurrentTimeMs(e.currentTarget.currentTime * 1000); setCurrentTimeMs(e.currentTarget.currentTime * 1000);
}} }}
+18 -8
View File
@@ -10,15 +10,14 @@ import { AckButton } from "./ack-button";
interface ParticleRendererProps { interface ParticleRendererProps {
particle: StreamParticle; particle: StreamParticle;
onNext: () => void;
onPrev: () => void;
} }
export function ParticleRenderer({ export function ParticleRenderer({
particle, particle,
onNext,
onPrev,
}: ParticleRendererProps) { }: ParticleRendererProps) {
const next = usePlaybackStore((s) => s.next);
const prev = usePlaybackStore((s) => s.prev);
const markParticlesSeen = useAppStore((s) => s.markParticlesSeen); const markParticlesSeen = useAppStore((s) => s.markParticlesSeen);
const markedRef = useRef<string | null>(null); const markedRef = useRef<string | null>(null);
@@ -33,15 +32,15 @@ export function ParticleRenderer({
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => { const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect(); const rect = e.currentTarget.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; const x = (e.clientX - rect.left) / rect.width;
if (x < 0.3) onPrev(); if (x < 0.3) prev();
else if (x > 0.7) onNext(); else if (x > 0.7) next();
}; };
const renderContent = () => { const renderContent = () => {
switch (particle.type) { switch (particle.type) {
case "media": case "media":
{/* NOTE: it's more robust to re-mount the MediaParticleView when the particle changes, to ensure playback state is well-behaved */} {/* NOTE: it's more robust to re-mount the MediaParticleView when the particle changes, to ensure playback state is well-behaved */ }
return <MediaParticleView key={particle.id} particle={particle} onEnded={onNext} />; return <MediaParticleView key={particle.id} particle={particle} />;
case "text": case "text":
return <TextParticleView particle={particle} />; return <TextParticleView particle={particle} />;
default: default:
@@ -61,3 +60,14 @@ export function ParticleRenderer({
</div> </div>
); );
} }
function RenderParticle({ particle }: { particle: StreamParticle }) {
switch (particle.type) {
case "media":
return <MediaParticleView particle={particle} />;
case "text":
return <TextParticleView particle={particle} />;
default:
return <FallbackParticleView particle={particle} />;
}
}
+1 -5
View File
@@ -211,11 +211,7 @@ export function StreamPlayerPage() {
{/* Particle content — fills entire viewport */} {/* Particle content — fills entire viewport */}
<div className="absolute inset-0"> <div className="absolute inset-0">
{currentParticle && ( {currentParticle && (
<ParticleRenderer <ParticleRenderer particle={currentParticle} />
particle={currentParticle}
onNext={next}
onPrev={prev}
/>
)} )}
</div> </div>